docxmlater 10.1.3 → 10.1.5

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 (371) hide show
  1. package/README.md +759 -754
  2. package/dist/constants/legacyCompatFlags.js +1 -1
  3. package/dist/constants/legacyCompatFlags.js.map +1 -1
  4. package/dist/constants/limits.js.map +1 -1
  5. package/dist/core/Document.d.ts +50 -50
  6. package/dist/core/Document.d.ts.map +1 -1
  7. package/dist/core/Document.js +483 -471
  8. package/dist/core/Document.js.map +1 -1
  9. package/dist/core/DocumentContent.d.ts +9 -9
  10. package/dist/core/DocumentContent.d.ts.map +1 -1
  11. package/dist/core/DocumentContent.js +1 -1
  12. package/dist/core/DocumentContent.js.map +1 -1
  13. package/dist/core/DocumentGenerator.d.ts +11 -11
  14. package/dist/core/DocumentGenerator.d.ts.map +1 -1
  15. package/dist/core/DocumentGenerator.js +251 -251
  16. package/dist/core/DocumentGenerator.js.map +1 -1
  17. package/dist/core/DocumentIdManager.js.map +1 -1
  18. package/dist/core/DocumentParser.d.ts +15 -15
  19. package/dist/core/DocumentParser.d.ts.map +1 -1
  20. package/dist/core/DocumentParser.js +2123 -2155
  21. package/dist/core/DocumentParser.js.map +1 -1
  22. package/dist/core/DocumentValidator.d.ts.map +1 -1
  23. package/dist/core/DocumentValidator.js +2 -5
  24. package/dist/core/DocumentValidator.js.map +1 -1
  25. package/dist/core/Relationship.js.map +1 -1
  26. package/dist/core/RelationshipManager.d.ts.map +1 -1
  27. package/dist/core/RelationshipManager.js +3 -3
  28. package/dist/core/RelationshipManager.js.map +1 -1
  29. package/dist/elements/AlternateContent.js.map +1 -1
  30. package/dist/elements/Bookmark.d.ts.map +1 -1
  31. package/dist/elements/Bookmark.js +3 -1
  32. package/dist/elements/Bookmark.js.map +1 -1
  33. package/dist/elements/BookmarkManager.d.ts.map +1 -1
  34. package/dist/elements/BookmarkManager.js.map +1 -1
  35. package/dist/elements/Comment.d.ts.map +1 -1
  36. package/dist/elements/Comment.js +9 -6
  37. package/dist/elements/Comment.js.map +1 -1
  38. package/dist/elements/CommentManager.d.ts.map +1 -1
  39. package/dist/elements/CommentManager.js +18 -17
  40. package/dist/elements/CommentManager.js.map +1 -1
  41. package/dist/elements/CommonTypes.d.ts +21 -21
  42. package/dist/elements/CommonTypes.d.ts.map +1 -1
  43. package/dist/elements/CommonTypes.js +56 -56
  44. package/dist/elements/CommonTypes.js.map +1 -1
  45. package/dist/elements/CustomXml.js.map +1 -1
  46. package/dist/elements/Endnote.d.ts.map +1 -1
  47. package/dist/elements/Endnote.js +6 -6
  48. package/dist/elements/Endnote.js.map +1 -1
  49. package/dist/elements/EndnoteManager.d.ts.map +1 -1
  50. package/dist/elements/EndnoteManager.js +6 -7
  51. package/dist/elements/EndnoteManager.js.map +1 -1
  52. package/dist/elements/Field.d.ts.map +1 -1
  53. package/dist/elements/Field.js +82 -25
  54. package/dist/elements/Field.js.map +1 -1
  55. package/dist/elements/FieldHelpers.d.ts.map +1 -1
  56. package/dist/elements/FieldHelpers.js.map +1 -1
  57. package/dist/elements/FontManager.d.ts.map +1 -1
  58. package/dist/elements/FontManager.js +1 -1
  59. package/dist/elements/FontManager.js.map +1 -1
  60. package/dist/elements/Footer.js +2 -2
  61. package/dist/elements/Footer.js.map +1 -1
  62. package/dist/elements/Footnote.d.ts.map +1 -1
  63. package/dist/elements/Footnote.js +6 -6
  64. package/dist/elements/Footnote.js.map +1 -1
  65. package/dist/elements/FootnoteManager.d.ts.map +1 -1
  66. package/dist/elements/FootnoteManager.js +6 -7
  67. package/dist/elements/FootnoteManager.js.map +1 -1
  68. package/dist/elements/Header.js +2 -2
  69. package/dist/elements/Header.js.map +1 -1
  70. package/dist/elements/HeaderFooterManager.js.map +1 -1
  71. package/dist/elements/Hyperlink.d.ts +5 -3
  72. package/dist/elements/Hyperlink.d.ts.map +1 -1
  73. package/dist/elements/Hyperlink.js +134 -76
  74. package/dist/elements/Hyperlink.js.map +1 -1
  75. package/dist/elements/Image.d.ts.map +1 -1
  76. package/dist/elements/Image.js +238 -106
  77. package/dist/elements/Image.js.map +1 -1
  78. package/dist/elements/ImageManager.d.ts.map +1 -1
  79. package/dist/elements/ImageManager.js +1 -1
  80. package/dist/elements/ImageManager.js.map +1 -1
  81. package/dist/elements/ImageRun.js +1 -1
  82. package/dist/elements/ImageRun.js.map +1 -1
  83. package/dist/elements/MathElement.js.map +1 -1
  84. package/dist/elements/Paragraph.d.ts +24 -24
  85. package/dist/elements/Paragraph.d.ts.map +1 -1
  86. package/dist/elements/Paragraph.js +181 -188
  87. package/dist/elements/Paragraph.js.map +1 -1
  88. package/dist/elements/PreservedElement.js.map +1 -1
  89. package/dist/elements/PropertyChangeTypes.d.ts.map +1 -1
  90. package/dist/elements/PropertyChangeTypes.js +6 -6
  91. package/dist/elements/PropertyChangeTypes.js.map +1 -1
  92. package/dist/elements/RangeMarker.d.ts.map +1 -1
  93. package/dist/elements/RangeMarker.js.map +1 -1
  94. package/dist/elements/Revision.d.ts.map +1 -1
  95. package/dist/elements/Revision.js +4 -5
  96. package/dist/elements/Revision.js.map +1 -1
  97. package/dist/elements/RevisionContent.js.map +1 -1
  98. package/dist/elements/RevisionManager.d.ts.map +1 -1
  99. package/dist/elements/RevisionManager.js +40 -48
  100. package/dist/elements/RevisionManager.js.map +1 -1
  101. package/dist/elements/Run.d.ts +16 -16
  102. package/dist/elements/Run.d.ts.map +1 -1
  103. package/dist/elements/Run.js +256 -238
  104. package/dist/elements/Run.js.map +1 -1
  105. package/dist/elements/Section.d.ts.map +1 -1
  106. package/dist/elements/Section.js +36 -11
  107. package/dist/elements/Section.js.map +1 -1
  108. package/dist/elements/Shape.d.ts.map +1 -1
  109. package/dist/elements/Shape.js.map +1 -1
  110. package/dist/elements/StructuredDocumentTag.d.ts +6 -6
  111. package/dist/elements/StructuredDocumentTag.d.ts.map +1 -1
  112. package/dist/elements/StructuredDocumentTag.js +99 -104
  113. package/dist/elements/StructuredDocumentTag.js.map +1 -1
  114. package/dist/elements/Table.d.ts +11 -11
  115. package/dist/elements/Table.d.ts.map +1 -1
  116. package/dist/elements/Table.js +102 -107
  117. package/dist/elements/Table.js.map +1 -1
  118. package/dist/elements/TableCell.d.ts +10 -10
  119. package/dist/elements/TableCell.d.ts.map +1 -1
  120. package/dist/elements/TableCell.js +105 -106
  121. package/dist/elements/TableCell.js.map +1 -1
  122. package/dist/elements/TableGridChange.d.ts.map +1 -1
  123. package/dist/elements/TableGridChange.js.map +1 -1
  124. package/dist/elements/TableOfContents.d.ts.map +1 -1
  125. package/dist/elements/TableOfContents.js +4 -4
  126. package/dist/elements/TableOfContents.js.map +1 -1
  127. package/dist/elements/TableOfContentsElement.js.map +1 -1
  128. package/dist/elements/TableRow.d.ts.map +1 -1
  129. package/dist/elements/TableRow.js +13 -6
  130. package/dist/elements/TableRow.js.map +1 -1
  131. package/dist/elements/TextBox.d.ts.map +1 -1
  132. package/dist/elements/TextBox.js +3 -5
  133. package/dist/elements/TextBox.js.map +1 -1
  134. package/dist/formatting/AbstractNumbering.d.ts +4 -4
  135. package/dist/formatting/AbstractNumbering.d.ts.map +1 -1
  136. package/dist/formatting/AbstractNumbering.js +54 -49
  137. package/dist/formatting/AbstractNumbering.js.map +1 -1
  138. package/dist/formatting/NumberingInstance.d.ts.map +1 -1
  139. package/dist/formatting/NumberingInstance.js +1 -3
  140. package/dist/formatting/NumberingInstance.js.map +1 -1
  141. package/dist/formatting/NumberingLevel.d.ts +5 -5
  142. package/dist/formatting/NumberingLevel.d.ts.map +1 -1
  143. package/dist/formatting/NumberingLevel.js +119 -125
  144. package/dist/formatting/NumberingLevel.js.map +1 -1
  145. package/dist/formatting/NumberingManager.d.ts.map +1 -1
  146. package/dist/formatting/NumberingManager.js +9 -9
  147. package/dist/formatting/NumberingManager.js.map +1 -1
  148. package/dist/formatting/Style.d.ts +11 -11
  149. package/dist/formatting/Style.d.ts.map +1 -1
  150. package/dist/formatting/Style.js +219 -247
  151. package/dist/formatting/Style.js.map +1 -1
  152. package/dist/formatting/StylesManager.d.ts +2 -2
  153. package/dist/formatting/StylesManager.d.ts.map +1 -1
  154. package/dist/formatting/StylesManager.js +96 -102
  155. package/dist/formatting/StylesManager.js.map +1 -1
  156. package/dist/helpers/CleanupHelper.d.ts +1 -1
  157. package/dist/helpers/CleanupHelper.d.ts.map +1 -1
  158. package/dist/helpers/CleanupHelper.js +6 -6
  159. package/dist/helpers/CleanupHelper.js.map +1 -1
  160. package/dist/images/ImageOptimizer.js +7 -7
  161. package/dist/images/ImageOptimizer.js.map +1 -1
  162. package/dist/index.d.ts +9 -9
  163. package/dist/index.d.ts.map +1 -1
  164. package/dist/index.js.map +1 -1
  165. package/dist/managers/DrawingManager.js.map +1 -1
  166. package/dist/tracking/DocumentTrackingContext.d.ts.map +1 -1
  167. package/dist/tracking/DocumentTrackingContext.js +23 -7
  168. package/dist/tracking/DocumentTrackingContext.js.map +1 -1
  169. package/dist/tracking/TrackingContext.d.ts.map +1 -1
  170. package/dist/tracking/TrackingContext.js.map +1 -1
  171. package/dist/types/compatibility-types.js.map +1 -1
  172. package/dist/types/formatting.js.map +1 -1
  173. package/dist/types/list-types.d.ts +6 -6
  174. package/dist/types/list-types.js.map +1 -1
  175. package/dist/types/settings-types.js.map +1 -1
  176. package/dist/types/styleConfig.d.ts +2 -2
  177. package/dist/types/styleConfig.js.map +1 -1
  178. package/dist/utils/ChangelogGenerator.d.ts.map +1 -1
  179. package/dist/utils/ChangelogGenerator.js +97 -101
  180. package/dist/utils/ChangelogGenerator.js.map +1 -1
  181. package/dist/utils/CompatibilityUpgrader.d.ts.map +1 -1
  182. package/dist/utils/CompatibilityUpgrader.js +1 -1
  183. package/dist/utils/CompatibilityUpgrader.js.map +1 -1
  184. package/dist/utils/InMemoryRevisionAcceptor.d.ts.map +1 -1
  185. package/dist/utils/InMemoryRevisionAcceptor.js +1 -6
  186. package/dist/utils/InMemoryRevisionAcceptor.js.map +1 -1
  187. package/dist/utils/MoveOperationHelper.d.ts.map +1 -1
  188. package/dist/utils/MoveOperationHelper.js +1 -1
  189. package/dist/utils/MoveOperationHelper.js.map +1 -1
  190. package/dist/utils/RevisionAwareProcessor.d.ts.map +1 -1
  191. package/dist/utils/RevisionAwareProcessor.js +2 -4
  192. package/dist/utils/RevisionAwareProcessor.js.map +1 -1
  193. package/dist/utils/RevisionWalker.d.ts.map +1 -1
  194. package/dist/utils/RevisionWalker.js +4 -12
  195. package/dist/utils/RevisionWalker.js.map +1 -1
  196. package/dist/utils/SelectiveRevisionAcceptor.d.ts.map +1 -1
  197. package/dist/utils/SelectiveRevisionAcceptor.js +2 -6
  198. package/dist/utils/SelectiveRevisionAcceptor.js.map +1 -1
  199. package/dist/utils/ShadingResolver.d.ts.map +1 -1
  200. package/dist/utils/ShadingResolver.js +1 -1
  201. package/dist/utils/ShadingResolver.js.map +1 -1
  202. package/dist/utils/acceptRevisions.d.ts.map +1 -1
  203. package/dist/utils/acceptRevisions.js +23 -12
  204. package/dist/utils/acceptRevisions.js.map +1 -1
  205. package/dist/utils/cnfStyleDecoder.d.ts +1 -1
  206. package/dist/utils/cnfStyleDecoder.d.ts.map +1 -1
  207. package/dist/utils/cnfStyleDecoder.js +40 -40
  208. package/dist/utils/cnfStyleDecoder.js.map +1 -1
  209. package/dist/utils/corruptionDetection.d.ts.map +1 -1
  210. package/dist/utils/corruptionDetection.js.map +1 -1
  211. package/dist/utils/dateFormatting.js.map +1 -1
  212. package/dist/utils/deepClone.js +1 -1
  213. package/dist/utils/deepClone.js.map +1 -1
  214. package/dist/utils/diagnostics.d.ts.map +1 -1
  215. package/dist/utils/diagnostics.js +1 -1
  216. package/dist/utils/diagnostics.js.map +1 -1
  217. package/dist/utils/errorHandling.js.map +1 -1
  218. package/dist/utils/formatting.d.ts.map +1 -1
  219. package/dist/utils/formatting.js +10 -2
  220. package/dist/utils/formatting.js.map +1 -1
  221. package/dist/utils/list-detection.d.ts +2 -2
  222. package/dist/utils/list-detection.d.ts.map +1 -1
  223. package/dist/utils/list-detection.js +21 -23
  224. package/dist/utils/list-detection.js.map +1 -1
  225. package/dist/utils/logger.d.ts.map +1 -1
  226. package/dist/utils/logger.js +12 -7
  227. package/dist/utils/logger.js.map +1 -1
  228. package/dist/utils/parsingHelpers.js.map +1 -1
  229. package/dist/utils/stripTrackedChanges.d.ts.map +1 -1
  230. package/dist/utils/stripTrackedChanges.js +3 -3
  231. package/dist/utils/stripTrackedChanges.js.map +1 -1
  232. package/dist/utils/textDiff.d.ts +1 -1
  233. package/dist/utils/textDiff.js +8 -8
  234. package/dist/utils/textDiff.js.map +1 -1
  235. package/dist/utils/units.js.map +1 -1
  236. package/dist/utils/validation.d.ts.map +1 -1
  237. package/dist/utils/validation.js +24 -7
  238. package/dist/utils/validation.js.map +1 -1
  239. package/dist/utils/xmlSanitization.d.ts.map +1 -1
  240. package/dist/utils/xmlSanitization.js +3 -3
  241. package/dist/utils/xmlSanitization.js.map +1 -1
  242. package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
  243. package/dist/validation/RevisionAutoFixer.js +5 -5
  244. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  245. package/dist/validation/RevisionValidator.d.ts.map +1 -1
  246. package/dist/validation/RevisionValidator.js +7 -9
  247. package/dist/validation/RevisionValidator.js.map +1 -1
  248. package/dist/validation/ValidationRules.js +3 -3
  249. package/dist/validation/ValidationRules.js.map +1 -1
  250. package/dist/validation/index.js.map +1 -1
  251. package/dist/xml/XMLBuilder.d.ts +1 -1
  252. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  253. package/dist/xml/XMLBuilder.js +98 -100
  254. package/dist/xml/XMLBuilder.js.map +1 -1
  255. package/dist/xml/XMLParser.d.ts.map +1 -1
  256. package/dist/xml/XMLParser.js +61 -66
  257. package/dist/xml/XMLParser.js.map +1 -1
  258. package/dist/zip/ZipHandler.d.ts.map +1 -1
  259. package/dist/zip/ZipHandler.js.map +1 -1
  260. package/dist/zip/ZipReader.d.ts.map +1 -1
  261. package/dist/zip/ZipReader.js +1 -3
  262. package/dist/zip/ZipReader.js.map +1 -1
  263. package/dist/zip/ZipWriter.d.ts +1 -1
  264. package/dist/zip/ZipWriter.d.ts.map +1 -1
  265. package/dist/zip/ZipWriter.js +28 -36
  266. package/dist/zip/ZipWriter.js.map +1 -1
  267. package/dist/zip/types.js +1 -1
  268. package/dist/zip/types.js.map +1 -1
  269. package/package.json +92 -92
  270. package/src/__tests__/helper-methods.test.ts +512 -512
  271. package/src/constants/legacyCompatFlags.ts +138 -138
  272. package/src/constants/limits.ts +50 -50
  273. package/src/core/Document.ts +985 -1145
  274. package/src/core/DocumentContent.ts +461 -467
  275. package/src/core/DocumentGenerator.ts +1133 -1104
  276. package/src/core/DocumentIdManager.ts +158 -158
  277. package/src/core/DocumentParser.ts +2347 -2716
  278. package/src/core/DocumentValidator.ts +363 -372
  279. package/src/core/Relationship.ts +367 -367
  280. package/src/core/RelationshipManager.ts +429 -428
  281. package/src/elements/AlternateContent.ts +42 -42
  282. package/src/elements/Bookmark.ts +212 -210
  283. package/src/elements/BookmarkManager.ts +247 -250
  284. package/src/elements/Comment.ts +356 -359
  285. package/src/elements/CommentManager.ts +499 -502
  286. package/src/elements/CommonTypes.ts +524 -549
  287. package/src/elements/CustomXml.ts +36 -36
  288. package/src/elements/Endnote.ts +221 -217
  289. package/src/elements/EndnoteManager.ts +246 -249
  290. package/src/elements/Field.ts +1292 -1233
  291. package/src/elements/FieldHelpers.ts +329 -333
  292. package/src/elements/FontManager.ts +336 -339
  293. package/src/elements/Footer.ts +269 -269
  294. package/src/elements/Footnote.ts +221 -217
  295. package/src/elements/FootnoteManager.ts +246 -249
  296. package/src/elements/Header.ts +269 -269
  297. package/src/elements/HeaderFooterManager.ts +219 -219
  298. package/src/elements/Hyperlink.ts +1288 -1193
  299. package/src/elements/Image.ts +1982 -1756
  300. package/src/elements/ImageManager.ts +437 -432
  301. package/src/elements/ImageRun.ts +59 -59
  302. package/src/elements/MathElement.ts +65 -65
  303. package/src/elements/Paragraph.ts +4347 -4287
  304. package/src/elements/PreservedElement.ts +53 -53
  305. package/src/elements/PropertyChangeTypes.ts +458 -442
  306. package/src/elements/RangeMarker.ts +382 -400
  307. package/src/elements/Revision.ts +1198 -1217
  308. package/src/elements/RevisionContent.ts +73 -73
  309. package/src/elements/RevisionManager.ts +1070 -1070
  310. package/src/elements/Run.ts +3103 -3073
  311. package/src/elements/Section.ts +1521 -1421
  312. package/src/elements/Shape.ts +884 -873
  313. package/src/elements/StructuredDocumentTag.ts +1176 -1207
  314. package/src/elements/Table.ts +2468 -2524
  315. package/src/elements/TableCell.ts +1617 -1621
  316. package/src/elements/TableGridChange.ts +149 -151
  317. package/src/elements/TableOfContents.ts +701 -691
  318. package/src/elements/TableOfContentsElement.ts +89 -89
  319. package/src/elements/TableRow.ts +960 -929
  320. package/src/elements/TextBox.ts +766 -768
  321. package/src/formatting/AbstractNumbering.ts +580 -579
  322. package/src/formatting/NumberingInstance.ts +295 -299
  323. package/src/formatting/NumberingLevel.ts +981 -1040
  324. package/src/formatting/NumberingManager.ts +833 -827
  325. package/src/formatting/Style.ts +1785 -1879
  326. package/src/formatting/StylesManager.ts +1090 -1130
  327. package/src/helpers/CleanupHelper.ts +524 -524
  328. package/src/images/ImageOptimizer.ts +274 -274
  329. package/src/index.ts +559 -554
  330. package/src/managers/DrawingManager.ts +319 -319
  331. package/src/tracking/DocumentTrackingContext.ts +687 -674
  332. package/src/tracking/TrackingContext.ts +175 -173
  333. package/src/types/compatibility-types.ts +49 -49
  334. package/src/types/formatting.ts +210 -210
  335. package/src/types/list-types.ts +14 -14
  336. package/src/types/settings-types.ts +59 -59
  337. package/src/types/styleConfig.ts +189 -189
  338. package/src/utils/ChangelogGenerator.ts +1583 -1581
  339. package/src/utils/CompatibilityUpgrader.ts +235 -237
  340. package/src/utils/InMemoryRevisionAcceptor.ts +691 -696
  341. package/src/utils/MoveOperationHelper.ts +233 -238
  342. package/src/utils/RevisionAwareProcessor.ts +518 -526
  343. package/src/utils/RevisionWalker.ts +427 -457
  344. package/src/utils/SelectiveRevisionAcceptor.ts +662 -683
  345. package/src/utils/ShadingResolver.ts +105 -107
  346. package/src/utils/acceptRevisions.ts +723 -714
  347. package/src/utils/cnfStyleDecoder.ts +212 -217
  348. package/src/utils/corruptionDetection.ts +346 -345
  349. package/src/utils/dateFormatting.ts +20 -20
  350. package/src/utils/deepClone.ts +77 -78
  351. package/src/utils/diagnostics.ts +125 -129
  352. package/src/utils/errorHandling.ts +80 -80
  353. package/src/utils/formatting.ts +220 -213
  354. package/src/utils/list-detection.ts +32 -42
  355. package/src/utils/logger.ts +412 -404
  356. package/src/utils/parsingHelpers.ts +190 -190
  357. package/src/utils/stripTrackedChanges.ts +356 -353
  358. package/src/utils/textDiff.ts +100 -100
  359. package/src/utils/units.ts +421 -421
  360. package/src/utils/validation.ts +553 -542
  361. package/src/utils/xmlSanitization.ts +179 -182
  362. package/src/validation/RevisionAutoFixer.ts +541 -542
  363. package/src/validation/RevisionValidator.ts +470 -460
  364. package/src/validation/ValidationRules.ts +338 -338
  365. package/src/validation/index.ts +30 -30
  366. package/src/xml/XMLBuilder.ts +857 -871
  367. package/src/xml/XMLParser.ts +877 -919
  368. package/src/zip/ZipHandler.ts +629 -637
  369. package/src/zip/ZipReader.ts +295 -299
  370. package/src/zip/ZipWriter.ts +374 -390
  371. package/src/zip/types.ts +116 -116
@@ -1,929 +1,960 @@
1
- /**
2
- * TableRow - Represents a row in a table
3
- */
4
-
5
- import { TableCell } from './TableCell';
6
- import { XMLBuilder, XMLElement } from '../xml/XMLBuilder';
7
- import { TableBorder, TableBorders } from './Table';
8
- import {
9
- BasicShadingPattern,
10
- RowJustification as CommonRowJustification,
11
- ShadingConfig,
12
- buildShadingAttributes,
13
- } from './CommonTypes';
14
- import { defaultLogger } from '../utils/logger';
15
-
16
- // ============================================================================
17
- // RE-EXPORTED TYPES (for backward compatibility)
18
- // ============================================================================
19
-
20
- /**
21
- * Row justification/alignment options
22
- * @see CommonTypes.RowJustification
23
- */
24
- export type RowJustification = CommonRowJustification;
25
-
26
- /**
27
- * Shading pattern values per ECMA-376
28
- * @see CommonTypes.BasicShadingPattern for the canonical definition
29
- */
30
- export type ShadingPattern = BasicShadingPattern;
31
-
32
- /**
33
- * Shading configuration
34
- * @see ShadingConfig in CommonTypes.ts for the canonical definition
35
- */
36
- export type Shading = ShadingConfig;
37
-
38
- /**
39
- * Table property exceptions - overrides table-level properties for this row
40
- * Per ECMA-376 Part 1 §17.4.61 (w:tblPrEx)
41
- */
42
- export interface TablePropertyExceptions {
43
- /** Border overrides for this row */
44
- borders?: TableBorders;
45
- /** Shading override for this row */
46
- shading?: Shading;
47
- /** Cell spacing override in twips */
48
- cellSpacing?: number;
49
- /** Table width override in twips */
50
- width?: number;
51
- /** Table indentation override in twips */
52
- indentation?: number;
53
- /** Table justification override */
54
- justification?: RowJustification;
55
- }
56
-
57
- /**
58
- * Row formatting options
59
- */
60
- export interface RowFormatting {
61
- height?: number; // Height in twips
62
- heightRule?: 'auto' | 'exact' | 'atLeast';
63
- isHeader?: boolean; // Whether this is a header row
64
- cantSplit?: boolean; // Prevent row from breaking across pages
65
- justification?: RowJustification; // Row justification/alignment
66
- hidden?: boolean; // Hide row
67
- gridBefore?: number; // Grid columns before first cell
68
- gridAfter?: number; // Grid columns after last cell
69
- tablePropertyExceptions?: TablePropertyExceptions; // Table property exceptions for this row
70
- wBefore?: number; // Width before row in twips (per ECMA-376 §17.4.83)
71
- wBeforeType?: string; // Width before type (dxa, pct, auto)
72
- wAfter?: number; // Width after row in twips (per ECMA-376 §17.4.82)
73
- wAfterType?: string; // Width after type (dxa, pct, auto)
74
- cellSpacing?: number; // Row-level cell spacing override in twips
75
- cellSpacingType?: string; // Cell spacing type (dxa, pct)
76
- cnfStyle?: string; // Conditional formatting bitmask (per ECMA-376 §17.3.1.8)
77
- divId?: number; // HTML div association (per ECMA-376 §17.4.9)
78
- }
79
-
80
- /**
81
- * Table row property change tracking (w:trPrChange)
82
- * Per ECMA-376 Part 1 §17.13.5.38
83
- */
84
- export interface TrPrChange {
85
- author: string;
86
- date: string;
87
- id: string;
88
- previousProperties: Record<string, any>;
89
- }
90
-
91
- /**
92
- * Represents a table row
93
- */
94
- export class TableRow {
95
- private cells: TableCell[] = [];
96
- private formatting: RowFormatting;
97
- /** Parent table reference (if row is inside a table) */
98
- private _parentTable?: import('./Table').Table;
99
- /** Tracking context for automatic change tracking */
100
- private trackingContext?: import('../tracking/TrackingContext').TrackingContext;
101
- /** Table row property change tracking (w:trPrChange) */
102
- private trPrChange?: TrPrChange;
103
-
104
- /**
105
- * Creates a new TableRow
106
- * @param cellCount - Number of cells to create (optional)
107
- * @param formatting - Row formatting options
108
- */
109
- constructor(cellCount?: number, formatting: RowFormatting = {}) {
110
- this.formatting = formatting;
111
-
112
- if (cellCount !== undefined && cellCount > 0) {
113
- for (let i = 0; i < cellCount; i++) {
114
- const cell = new TableCell();
115
- cell._setParentRow(this);
116
- this.cells.push(cell);
117
- }
118
- }
119
- }
120
-
121
- /**
122
- * Sets the tracking context for automatic change tracking.
123
- * Called by Document when track changes is enabled.
124
- * @internal
125
- */
126
- _setTrackingContext(context: import('../tracking/TrackingContext').TrackingContext): void {
127
- this.trackingContext = context;
128
- }
129
-
130
- /**
131
- * Gets the table row property change tracking info
132
- */
133
- getTrPrChange(): TrPrChange | undefined {
134
- return this.trPrChange;
135
- }
136
-
137
- /**
138
- * Sets the table row property change tracking info
139
- */
140
- setTrPrChange(change: TrPrChange | undefined): void {
141
- this.trPrChange = change;
142
- }
143
-
144
- /**
145
- * Clears the table row property change tracking
146
- */
147
- clearTrPrChange(): void {
148
- this.trPrChange = undefined;
149
- }
150
-
151
- /**
152
- * Adds a cell to the row
153
- * @param cell - Cell to add
154
- * @returns This row for chaining
155
- */
156
- addCell(cell: TableCell): this {
157
- this.cells.push(cell);
158
- cell._setParentRow(this);
159
- return this;
160
- }
161
-
162
- /**
163
- * Creates and adds a new cell
164
- * @param text - Optional text content for the cell
165
- * @returns The created cell
166
- */
167
- createCell(text?: string): TableCell {
168
- const cell = new TableCell();
169
- if (text) {
170
- cell.createParagraph(text);
171
- }
172
- this.cells.push(cell);
173
- cell._setParentRow(this);
174
- return cell;
175
- }
176
-
177
- /**
178
- * Gets a cell by index
179
- * @param index - Cell index (0-based)
180
- * @returns The cell at the index, or undefined
181
- */
182
- getCell(index: number): TableCell | undefined {
183
- return this.cells[index];
184
- }
185
-
186
- /**
187
- * Inserts a cell at the specified index
188
- * @param index - Position to insert (0-based)
189
- * @param cell - Cell to insert
190
- */
191
- insertCellAt(index: number, cell: TableCell): void {
192
- this.cells.splice(index, 0, cell);
193
- cell._setParentRow(this);
194
- }
195
-
196
- /**
197
- * Removes and returns the cell at the specified index
198
- * @param index - Position to remove (0-based)
199
- * @returns The removed cell, or undefined if index is out of bounds
200
- */
201
- removeCellAt(index: number): TableCell | undefined {
202
- if (index < 0 || index >= this.cells.length) return undefined;
203
- const removed = this.cells.splice(index, 1);
204
- const cell = removed[0];
205
- if (cell) cell._setParentRow(undefined);
206
- return cell;
207
- }
208
-
209
- /**
210
- * Gets all cells in the row
211
- * @returns Array of cells
212
- */
213
- getCells(): TableCell[] {
214
- return [...this.cells];
215
- }
216
-
217
- /**
218
- * Gets the number of cells in the row
219
- * @returns Number of cells
220
- */
221
- getCellCount(): number {
222
- return this.cells.length;
223
- }
224
-
225
- /**
226
- * Calculates the total grid span of the row (considering column spans)
227
- *
228
- * For tables with merged cells, this returns the number of logical columns
229
- * this row spans based on the columnSpan values of each cell.
230
- *
231
- * @returns Total grid span (sum of all cell spans, where unspanned cells count as 1)
232
- *
233
- * @example
234
- * ```typescript
235
- * // Row with 3 cells, middle one spanning 2 columns
236
- * const row = new TableRow();
237
- * row.createCell('A'); // span = 1
238
- * row.createCell('B').setColumnSpan(2); // span = 2
239
- * row.createCell('C'); // span = 1
240
- * row.getTotalGridSpan(); // Returns 4
241
- * ```
242
- */
243
- getTotalGridSpan(): number {
244
- let totalSpan = 0;
245
- for (const cell of this.cells) {
246
- const formatting = cell.getFormatting();
247
- totalSpan += formatting.columnSpan || 1;
248
- }
249
- return totalSpan;
250
- }
251
-
252
- /**
253
- * Validates the row's grid alignment
254
- *
255
- * Checks if the total grid span matches the expected column count.
256
- * Logs a warning if there's a mismatch, which can indicate:
257
- * - Missing cells (grid span < expected)
258
- * - Extra cells (grid span > expected)
259
- * - Incorrect columnSpan values
260
- *
261
- * @param expectedColumns - Expected number of columns in the table grid
262
- * @returns Object with validation result and details
263
- *
264
- * @example
265
- * ```typescript
266
- * const result = row.validateGridAlignment(4);
267
- * if (!result.isValid) {
268
- * console.log(result.message); // "Row has 3 grid columns but expected 4"
269
- * }
270
- * ```
271
- */
272
- validateGridAlignment(expectedColumns: number): {
273
- isValid: boolean;
274
- actualSpan: number;
275
- message?: string;
276
- } {
277
- const actualSpan = this.getTotalGridSpan();
278
-
279
- if (actualSpan === expectedColumns) {
280
- return { isValid: true, actualSpan };
281
- }
282
-
283
- const message =
284
- `Row grid alignment mismatch: has ${actualSpan} grid columns but expected ${expectedColumns}. ` +
285
- `Cell count: ${this.cells.length}. ` +
286
- (actualSpan < expectedColumns
287
- ? "Missing cells or incorrect columnSpan values."
288
- : "Extra cells or excessive columnSpan values.");
289
-
290
- defaultLogger.warn(`[TableRow] ${message}`);
291
-
292
- return {
293
- isValid: false,
294
- actualSpan,
295
- message,
296
- };
297
- }
298
-
299
- /**
300
- * Sets row height
301
- * @param twips - Height in twips
302
- * @param rule - Height rule
303
- * @returns This row for chaining
304
- */
305
- setHeight(twips: number, rule: RowFormatting['heightRule'] = 'atLeast'): this {
306
- const prevHeight = this.formatting.height;
307
- const prevRule = this.formatting.heightRule;
308
- this.formatting.height = twips;
309
- this.formatting.heightRule = rule;
310
- if (this.trackingContext?.isEnabled()) {
311
- if (prevHeight !== twips) {
312
- this.trackingContext.trackTableChange(this, 'height', prevHeight, twips);
313
- }
314
- if (prevRule !== rule) {
315
- this.trackingContext.trackTableChange(this, 'heightRule', prevRule, rule);
316
- }
317
- }
318
- return this;
319
- }
320
-
321
- /**
322
- * Clears the row height, allowing Word to auto-size the row based on content
323
- * @returns This row for chaining
324
- */
325
- clearHeight(): this {
326
- delete this.formatting.height;
327
- delete this.formatting.heightRule;
328
- return this;
329
- }
330
-
331
- /**
332
- * Sets whether this is a header row
333
- * @param isHeader - Whether this is a header row
334
- * @returns This row for chaining
335
- */
336
- setHeader(isHeader = true): this {
337
- const prev = this.formatting.isHeader;
338
- this.formatting.isHeader = isHeader;
339
- if (this.trackingContext?.isEnabled() && prev !== isHeader) {
340
- this.trackingContext.trackTableChange(this, 'isHeader', prev, isHeader);
341
- }
342
- return this;
343
- }
344
-
345
- /**
346
- * Sets whether row can split across pages
347
- * @param cantSplit - Whether to prevent splitting
348
- * @returns This row for chaining
349
- */
350
- setCantSplit(cantSplit = true): this {
351
- const prev = this.formatting.cantSplit;
352
- this.formatting.cantSplit = cantSplit;
353
- if (this.trackingContext?.isEnabled() && prev !== cantSplit) {
354
- this.trackingContext.trackTableChange(this, 'cantSplit', prev, cantSplit);
355
- }
356
- return this;
357
- }
358
-
359
- /**
360
- * Sets row justification/alignment
361
- * Per ECMA-376 Part 1 §17.4.79 (w:jc)
362
- * @param alignment - Row justification ('left' | 'center' | 'right' | 'start' | 'end')
363
- * @returns This row for chaining
364
- * @example
365
- * ```typescript
366
- * row.setJustification('center'); // Center-align the entire row
367
- * ```
368
- */
369
- setJustification(alignment: RowJustification): this {
370
- const prev = this.formatting.justification;
371
- this.formatting.justification = alignment;
372
- if (this.trackingContext?.isEnabled() && prev !== alignment) {
373
- this.trackingContext.trackTableChange(this, 'justification', prev, alignment);
374
- }
375
- return this;
376
- }
377
-
378
- /**
379
- * Sets whether row is hidden
380
- * Per ECMA-376 Part 1 §17.4.23 (w:hidden)
381
- * @param hidden - Whether to hide the row
382
- * @returns This row for chaining
383
- * @example
384
- * ```typescript
385
- * row.setHidden(true); // Hide this row from display
386
- * ```
387
- */
388
- setHidden(hidden = true): this {
389
- const prev = this.formatting.hidden;
390
- this.formatting.hidden = hidden;
391
- if (this.trackingContext?.isEnabled() && prev !== hidden) {
392
- this.trackingContext.trackTableChange(this, 'hidden', prev, hidden);
393
- }
394
- return this;
395
- }
396
-
397
- /**
398
- * Sets grid columns before first cell
399
- * Per ECMA-376 Part 1 §17.4.15 (w:gridBefore)
400
- * Specifies number of grid columns that must be skipped before the first cell
401
- * @param columns - Number of grid columns to skip before first cell
402
- * @returns This row for chaining
403
- * @example
404
- * ```typescript
405
- * row.setGridBefore(2); // Skip 2 columns before first cell
406
- * ```
407
- */
408
- setGridBefore(columns: number): this {
409
- const prev = this.formatting.gridBefore;
410
- this.formatting.gridBefore = columns;
411
- if (this.trackingContext?.isEnabled() && prev !== columns) {
412
- this.trackingContext.trackTableChange(this, 'gridBefore', prev, columns);
413
- }
414
- return this;
415
- }
416
-
417
- /**
418
- * Sets grid columns after last cell
419
- * Per ECMA-376 Part 1 §17.4.14 (w:gridAfter)
420
- * Specifies number of grid columns that must be left after the last cell
421
- * @param columns - Number of grid columns to leave after last cell
422
- * @returns This row for chaining
423
- * @example
424
- * ```typescript
425
- * row.setGridAfter(1); // Leave 1 column after last cell
426
- * ```
427
- */
428
- setGridAfter(columns: number): this {
429
- const prev = this.formatting.gridAfter;
430
- this.formatting.gridAfter = columns;
431
- if (this.trackingContext?.isEnabled() && prev !== columns) {
432
- this.trackingContext.trackTableChange(this, 'gridAfter', prev, columns);
433
- }
434
- return this;
435
- }
436
-
437
- /**
438
- * Sets table property exceptions for this row
439
- * Per ECMA-376 Part 1 §17.4.61 (w:tblPrEx)
440
- *
441
- * Allows this row to override table-level properties like borders, shading, and cell spacing.
442
- * Typically used when merging tables or preserving formatting from legacy documents.
443
- *
444
- * @param exceptions - Table property exceptions configuration
445
- * @returns This row for chaining
446
- * @example
447
- * ```typescript
448
- * // Override borders for this row
449
- * row.setTablePropertyExceptions({
450
- * borders: {
451
- * top: { style: 'single', size: 8, color: 'FF0000' },
452
- * bottom: { style: 'single', size: 8, color: 'FF0000' }
453
- * },
454
- * shading: { fill: 'FFFF00', pattern: 'clear' }
455
- * });
456
- * ```
457
- */
458
- setTablePropertyExceptions(exceptions: TablePropertyExceptions): this {
459
- const prev = this.formatting.tablePropertyExceptions;
460
- this.formatting.tablePropertyExceptions = exceptions;
461
- if (this.trackingContext?.isEnabled() && prev !== exceptions) {
462
- this.trackingContext.trackTableChange(this, 'tablePropertyExceptions', prev, exceptions);
463
- }
464
- return this;
465
- }
466
-
467
- /**
468
- * Sets width before row (w:wBefore) per ECMA-376 Part 1 §17.4.83
469
- * @param width - Width in twips
470
- * @param type - Width type (dxa, pct, auto)
471
- * @returns This row for chaining
472
- */
473
- setWBefore(width: number, type = 'dxa'): this {
474
- const prevWidth = this.formatting.wBefore;
475
- const prevType = this.formatting.wBeforeType;
476
- this.formatting.wBefore = width;
477
- this.formatting.wBeforeType = type;
478
- if (this.trackingContext?.isEnabled()) {
479
- if (prevWidth !== width) {
480
- this.trackingContext.trackTableChange(this, 'wBefore', prevWidth, width);
481
- }
482
- if (prevType !== type) {
483
- this.trackingContext.trackTableChange(this, 'wBeforeType', prevType, type);
484
- }
485
- }
486
- return this;
487
- }
488
-
489
- /**
490
- * Sets width after row (w:wAfter) per ECMA-376 Part 1 §17.4.82
491
- * @param width - Width in twips
492
- * @param type - Width type (dxa, pct, auto)
493
- * @returns This row for chaining
494
- */
495
- setWAfter(width: number, type = 'dxa'): this {
496
- const prevWidth = this.formatting.wAfter;
497
- const prevType = this.formatting.wAfterType;
498
- this.formatting.wAfter = width;
499
- this.formatting.wAfterType = type;
500
- if (this.trackingContext?.isEnabled()) {
501
- if (prevWidth !== width) {
502
- this.trackingContext.trackTableChange(this, 'wAfter', prevWidth, width);
503
- }
504
- if (prevType !== type) {
505
- this.trackingContext.trackTableChange(this, 'wAfterType', prevType, type);
506
- }
507
- }
508
- return this;
509
- }
510
-
511
- /**
512
- * Sets row-level cell spacing override (w:tblCellSpacing on row)
513
- * @param spacing - Cell spacing in twips
514
- * @param type - Spacing type (dxa, pct)
515
- * @returns This row for chaining
516
- */
517
- setRowCellSpacing(spacing: number, type = 'dxa'): this {
518
- const prevSpacing = this.formatting.cellSpacing;
519
- const prevType = this.formatting.cellSpacingType;
520
- this.formatting.cellSpacing = spacing;
521
- this.formatting.cellSpacingType = type;
522
- if (this.trackingContext?.isEnabled()) {
523
- if (prevSpacing !== spacing) {
524
- this.trackingContext.trackTableChange(this, 'cellSpacing', prevSpacing, spacing);
525
- }
526
- if (prevType !== type) {
527
- this.trackingContext.trackTableChange(this, 'cellSpacingType', prevType, type);
528
- }
529
- }
530
- return this;
531
- }
532
-
533
- /**
534
- * Sets conditional formatting bitmask for this row (w:cnfStyle)
535
- * Per ECMA-376 Part 1 §17.3.1.8
536
- * @param cnfStyle - Binary string (e.g., '100000000000' for firstRow)
537
- * @returns This row for chaining
538
- */
539
- setCnfStyle(cnfStyle: string): this {
540
- const prev = this.formatting.cnfStyle;
541
- this.formatting.cnfStyle = cnfStyle;
542
- if (this.trackingContext?.isEnabled() && prev !== cnfStyle) {
543
- this.trackingContext.trackTableChange(this, 'cnfStyle', prev, cnfStyle);
544
- }
545
- return this;
546
- }
547
-
548
- /**
549
- * Sets the HTML div ID for web round-trip
550
- * Per ECMA-376 Part 1 §17.4.9
551
- * @param id - Div ID number
552
- * @returns This row for chaining
553
- */
554
- setDivId(id: number): this {
555
- this.formatting.divId = id;
556
- return this;
557
- }
558
-
559
- /**
560
- * Gets the HTML div ID
561
- * @returns Div ID or undefined
562
- */
563
- getDivId(): number | undefined {
564
- return this.formatting.divId;
565
- }
566
-
567
- /**
568
- * Gets table property exceptions
569
- * @returns Table property exceptions or undefined
570
- */
571
- getTablePropertyExceptions(): TablePropertyExceptions | undefined {
572
- return this.formatting.tablePropertyExceptions;
573
- }
574
-
575
- /**
576
- * Gets the row formatting
577
- * @returns Row formatting
578
- */
579
- getFormatting(): RowFormatting {
580
- return { ...this.formatting };
581
- }
582
-
583
- // ============================================================================
584
- // Individual Formatting Getters
585
- // ============================================================================
586
-
587
- /**
588
- * Gets the row height in twips
589
- * @returns Height in twips or undefined if not set
590
- */
591
- getHeight(): number | undefined {
592
- return this.formatting.height;
593
- }
594
-
595
- /**
596
- * Gets the row height rule
597
- * @returns Height rule ('auto', 'exact', 'atLeast') or undefined
598
- */
599
- getHeightRule(): string | undefined {
600
- return this.formatting.heightRule;
601
- }
602
-
603
- /**
604
- * Checks if this row is marked as a header row
605
- * @returns True if this is a header row
606
- */
607
- getIsHeader(): boolean {
608
- return this.formatting.isHeader ?? false;
609
- }
610
-
611
- /**
612
- * Gets whether the row can split across pages
613
- * @returns True if row cannot split
614
- */
615
- getCantSplit(): boolean {
616
- return this.formatting.cantSplit ?? false;
617
- }
618
-
619
- /**
620
- * Gets the row justification (alignment)
621
- * @returns Justification ('left', 'center', 'right') or undefined
622
- */
623
- getJustification(): string | undefined {
624
- return this.formatting.justification;
625
- }
626
-
627
- /**
628
- * Checks if this row is hidden
629
- * @returns True if row is hidden
630
- */
631
- isHidden(): boolean {
632
- return this.formatting.hidden ?? false;
633
- }
634
-
635
- /**
636
- * Sets the parent table reference for this row.
637
- * Called by Table when adding rows.
638
- * @internal
639
- */
640
- _setParentTable(table: import('./Table').Table | undefined): void {
641
- this._parentTable = table;
642
- }
643
-
644
- /**
645
- * Gets the parent table reference for this row.
646
- * @internal
647
- */
648
- _getParentTable(): import('./Table').Table | undefined {
649
- return this._parentTable;
650
- }
651
-
652
- /**
653
- * Builds XML for table property exceptions
654
- * Per ECMA-376 Part 1 §17.4.61
655
- * @private
656
- */
657
- private buildTablePropertyExceptionsXML(exceptions: TablePropertyExceptions): XMLElement[] {
658
- const children: XMLElement[] = [];
659
-
660
- // Add table width exception (w:tblW)
661
- if (exceptions.width !== undefined) {
662
- children.push(XMLBuilder.wSelf('tblW', {
663
- 'w:w': exceptions.width,
664
- 'w:type': 'dxa'
665
- }));
666
- }
667
-
668
- // Add table justification exception (w:jc)
669
- if (exceptions.justification) {
670
- children.push(XMLBuilder.wSelf('jc', { 'w:val': exceptions.justification }));
671
- }
672
-
673
- // Add cell spacing exception (w:tblCellSpacing)
674
- if (exceptions.cellSpacing !== undefined) {
675
- children.push(XMLBuilder.wSelf('tblCellSpacing', {
676
- 'w:w': exceptions.cellSpacing,
677
- 'w:type': 'dxa'
678
- }));
679
- }
680
-
681
- // Add table indentation exception (w:tblInd)
682
- if (exceptions.indentation !== undefined) {
683
- children.push(XMLBuilder.wSelf('tblInd', {
684
- 'w:w': exceptions.indentation,
685
- 'w:type': 'dxa'
686
- }));
687
- }
688
-
689
- // Add table borders exception (w:tblBorders)
690
- if (exceptions.borders) {
691
- const borderChildren = this.buildBordersXML(exceptions.borders);
692
- if (borderChildren.length > 0) {
693
- children.push(XMLBuilder.w('tblBorders', undefined, borderChildren));
694
- }
695
- }
696
-
697
- // Add shading exception (w:shd)
698
- if (exceptions.shading) {
699
- const shdAttrs: Record<string, string> = {};
700
- if (exceptions.shading.pattern) shdAttrs['w:val'] = exceptions.shading.pattern;
701
- if (exceptions.shading.color) shdAttrs['w:color'] = exceptions.shading.color;
702
- if (exceptions.shading.fill) shdAttrs['w:fill'] = exceptions.shading.fill;
703
- if (exceptions.shading.themeColor) shdAttrs['w:themeColor'] = exceptions.shading.themeColor;
704
- if (exceptions.shading.themeFill) shdAttrs['w:themeFill'] = exceptions.shading.themeFill;
705
- if (exceptions.shading.themeFillShade) shdAttrs['w:themeFillShade'] = exceptions.shading.themeFillShade;
706
- if (exceptions.shading.themeFillTint) shdAttrs['w:themeFillTint'] = exceptions.shading.themeFillTint;
707
- if (exceptions.shading.themeShade) shdAttrs['w:themeShade'] = exceptions.shading.themeShade;
708
- if (exceptions.shading.themeTint) shdAttrs['w:themeTint'] = exceptions.shading.themeTint;
709
- children.push(XMLBuilder.w('shd', shdAttrs));
710
- }
711
-
712
- return children;
713
- }
714
-
715
- /**
716
- * Builds XML for table borders
717
- * @private
718
- */
719
- private buildBordersXML(borders: TableBorders): XMLElement[] {
720
- const children: XMLElement[] = [];
721
-
722
- const borderNames: (keyof TableBorders)[] = ['top', 'left', 'bottom', 'right', 'insideH', 'insideV'];
723
-
724
- for (const name of borderNames) {
725
- const border = borders[name];
726
- if (border) {
727
- const attrs: Record<string, string | number> = {};
728
- if (border.style) attrs['w:val'] = border.style;
729
- if (border.size !== undefined) attrs['w:sz'] = border.size;
730
- if (border.space !== undefined) attrs['w:space'] = border.space;
731
- if (border.color) attrs['w:color'] = border.color;
732
-
733
- if (Object.keys(attrs).length > 0) {
734
- children.push(XMLBuilder.wSelf(name, attrs));
735
- }
736
- }
737
- }
738
-
739
- return children;
740
- }
741
-
742
- /**
743
- * Converts the row to WordprocessingML XML element
744
- * @returns XMLElement representing the row
745
- */
746
- toXML(): XMLElement {
747
- const trPrChildren: XMLElement[] = [];
748
-
749
- // Ordered per CT_TrPr (ECMA-376 §17.4.79):
750
- // cnfStyle → divId → gridBefore → gridAfter → wBefore → wAfter →
751
- // cantSplit → trHeight → tblHeader → tblCellSpacing → jc → hidden
752
-
753
- // 1. cnfStyle (conditional formatting bitmask)
754
- if (this.formatting.cnfStyle) {
755
- trPrChildren.push(XMLBuilder.wSelf('cnfStyle', { 'w:val': this.formatting.cnfStyle }));
756
- }
757
-
758
- // 2. divId
759
- if (this.formatting.divId !== undefined) {
760
- trPrChildren.push(XMLBuilder.wSelf('divId', { 'w:val': this.formatting.divId }));
761
- }
762
-
763
- // 3. gridBefore
764
- if (this.formatting.gridBefore !== undefined) {
765
- trPrChildren.push(XMLBuilder.wSelf('gridBefore', { 'w:val': this.formatting.gridBefore }));
766
- }
767
-
768
- // 4. gridAfter
769
- if (this.formatting.gridAfter !== undefined) {
770
- trPrChildren.push(XMLBuilder.wSelf('gridAfter', { 'w:val': this.formatting.gridAfter }));
771
- }
772
-
773
- // 5. wBefore
774
- if (this.formatting.wBefore !== undefined) {
775
- trPrChildren.push(XMLBuilder.wSelf('wBefore', {
776
- 'w:w': this.formatting.wBefore,
777
- 'w:type': this.formatting.wBeforeType || 'dxa',
778
- }));
779
- }
780
-
781
- // 6. wAfter
782
- if (this.formatting.wAfter !== undefined) {
783
- trPrChildren.push(XMLBuilder.wSelf('wAfter', {
784
- 'w:w': this.formatting.wAfter,
785
- 'w:type': this.formatting.wAfterType || 'dxa',
786
- }));
787
- }
788
-
789
- // 7. cantSplit
790
- if (this.formatting.cantSplit) {
791
- trPrChildren.push(XMLBuilder.wSelf('cantSplit'));
792
- }
793
-
794
- // 8. trHeight
795
- if (this.formatting.height !== undefined) {
796
- const attrs: Record<string, string | number> = {
797
- 'w:val': this.formatting.height,
798
- };
799
- if (this.formatting.heightRule) {
800
- attrs['w:hRule'] = this.formatting.heightRule;
801
- }
802
- trPrChildren.push(XMLBuilder.wSelf('trHeight', attrs));
803
- }
804
-
805
- // 9. tblHeader
806
- if (this.formatting.isHeader) {
807
- trPrChildren.push(XMLBuilder.wSelf('tblHeader'));
808
- }
809
-
810
- // 10. tblCellSpacing
811
- if (this.formatting.cellSpacing !== undefined) {
812
- trPrChildren.push(XMLBuilder.wSelf('tblCellSpacing', {
813
- 'w:w': this.formatting.cellSpacing,
814
- 'w:type': this.formatting.cellSpacingType || 'dxa',
815
- }));
816
- }
817
-
818
- // 11. jc (map 'start'/'end' to valid ST_JcTable values)
819
- if (this.formatting.justification) {
820
- const jcMap: Record<string, string> = { start: 'left', end: 'right' };
821
- const jcVal = jcMap[this.formatting.justification] || this.formatting.justification;
822
- trPrChildren.push(XMLBuilder.wSelf('jc', { 'w:val': jcVal }));
823
- }
824
-
825
- // 12. hidden
826
- if (this.formatting.hidden) {
827
- trPrChildren.push(XMLBuilder.wSelf('hidden'));
828
- }
829
-
830
- // Add table row property change (w:trPrChange) per ECMA-376 Part 1 §17.13.5.38
831
- // Must be last child of w:trPr
832
- if (this.trPrChange) {
833
- const changeAttrs: Record<string, string | number> = {
834
- 'w:id': this.trPrChange.id,
835
- 'w:author': this.trPrChange.author,
836
- 'w:date': this.trPrChange.date,
837
- };
838
- const prevTrPrChildren: XMLElement[] = [];
839
- const prev = this.trPrChange.previousProperties;
840
- if (prev) {
841
- // Ordered per CT_TrPr: cnfStyle gridBefore gridAfter wBefore wAfter →
842
- // cantSplit trHeight tblHeader → tblCellSpacing → jc → hidden
843
- if (prev.cnfStyle) {
844
- prevTrPrChildren.push(XMLBuilder.wSelf('cnfStyle', { 'w:val': prev.cnfStyle }));
845
- }
846
- if (prev.gridBefore !== undefined) {
847
- prevTrPrChildren.push(XMLBuilder.wSelf('gridBefore', { 'w:val': prev.gridBefore }));
848
- }
849
- if (prev.gridAfter !== undefined) {
850
- prevTrPrChildren.push(XMLBuilder.wSelf('gridAfter', { 'w:val': prev.gridAfter }));
851
- }
852
- if (prev.wBefore !== undefined) {
853
- prevTrPrChildren.push(XMLBuilder.wSelf('wBefore', {
854
- 'w:w': prev.wBefore,
855
- 'w:type': prev.wBeforeType || 'dxa',
856
- }));
857
- }
858
- if (prev.wAfter !== undefined) {
859
- prevTrPrChildren.push(XMLBuilder.wSelf('wAfter', {
860
- 'w:w': prev.wAfter,
861
- 'w:type': prev.wAfterType || 'dxa',
862
- }));
863
- }
864
- if (prev.cantSplit) {
865
- prevTrPrChildren.push(XMLBuilder.wSelf('cantSplit'));
866
- }
867
- if (prev.height !== undefined) {
868
- const heightAttrs: Record<string, string | number> = { 'w:val': prev.height };
869
- if (prev.heightRule) heightAttrs['w:hRule'] = prev.heightRule;
870
- prevTrPrChildren.push(XMLBuilder.wSelf('trHeight', heightAttrs));
871
- }
872
- if (prev.isHeader) {
873
- prevTrPrChildren.push(XMLBuilder.wSelf('tblHeader'));
874
- }
875
- if (prev.cellSpacing !== undefined) {
876
- prevTrPrChildren.push(XMLBuilder.wSelf('tblCellSpacing', {
877
- 'w:w': prev.cellSpacing,
878
- 'w:type': prev.cellSpacingType || 'dxa',
879
- }));
880
- }
881
- if (prev.justification) {
882
- const jcPrevMap: Record<string, string> = { start: 'left', end: 'right' };
883
- prevTrPrChildren.push(XMLBuilder.wSelf('jc', { 'w:val': jcPrevMap[prev.justification] || prev.justification }));
884
- }
885
- if (prev.hidden) {
886
- prevTrPrChildren.push(XMLBuilder.wSelf('hidden'));
887
- }
888
- }
889
- const prevTrPr = XMLBuilder.w('trPr', undefined, prevTrPrChildren);
890
- trPrChildren.push(XMLBuilder.w('trPrChange', changeAttrs, [prevTrPr]));
891
- }
892
-
893
- // Build row element
894
- const rowChildren: XMLElement[] = [];
895
-
896
- // Add row properties if there are any
897
- if (trPrChildren.length > 0) {
898
- rowChildren.push(XMLBuilder.w('trPr', undefined, trPrChildren));
899
- }
900
-
901
- // Add table property exceptions (tblPrEx) if present
902
- if (this.formatting.tablePropertyExceptions) {
903
- const tblPrExChildren = this.buildTablePropertyExceptionsXML(this.formatting.tablePropertyExceptions);
904
- if (tblPrExChildren.length > 0) {
905
- rowChildren.push(XMLBuilder.w('tblPrEx', undefined, tblPrExChildren));
906
- }
907
- }
908
-
909
- // Add all cells - each cell is independent
910
- // Note: gridSpan (columnSpan) means a single cell spans multiple columns in the grid,
911
- // it does NOT mean subsequent cells should be skipped. Each cell in the array
912
- // represents a distinct cell that should be output to the XML.
913
- for (const cell of this.cells) {
914
- rowChildren.push(cell.toXML());
915
- }
916
-
917
- return XMLBuilder.w('tr', undefined, rowChildren);
918
- }
919
-
920
- /**
921
- * Creates a new TableRow
922
- * @param cellCount - Number of cells to create
923
- * @param formatting - Row formatting
924
- * @returns New TableRow instance
925
- */
926
- static create(cellCount?: number, formatting?: RowFormatting): TableRow {
927
- return new TableRow(cellCount, formatting);
928
- }
929
- }
1
+ /**
2
+ * TableRow - Represents a row in a table
3
+ */
4
+
5
+ import { TableCell } from './TableCell';
6
+ import { XMLBuilder, XMLElement } from '../xml/XMLBuilder';
7
+ import { TableBorder, TableBorders } from './Table';
8
+ import {
9
+ BasicShadingPattern,
10
+ RowJustification as CommonRowJustification,
11
+ ShadingConfig,
12
+ buildShadingAttributes,
13
+ } from './CommonTypes';
14
+ import { defaultLogger } from '../utils/logger';
15
+
16
+ // ============================================================================
17
+ // RE-EXPORTED TYPES (for backward compatibility)
18
+ // ============================================================================
19
+
20
+ /**
21
+ * Row justification/alignment options
22
+ * @see CommonTypes.RowJustification
23
+ */
24
+ export type RowJustification = CommonRowJustification;
25
+
26
+ /**
27
+ * Shading pattern values per ECMA-376
28
+ * @see CommonTypes.BasicShadingPattern for the canonical definition
29
+ */
30
+ export type ShadingPattern = BasicShadingPattern;
31
+
32
+ /**
33
+ * Shading configuration
34
+ * @see ShadingConfig in CommonTypes.ts for the canonical definition
35
+ */
36
+ export type Shading = ShadingConfig;
37
+
38
+ /**
39
+ * Table property exceptions - overrides table-level properties for this row
40
+ * Per ECMA-376 Part 1 §17.4.61 (w:tblPrEx)
41
+ */
42
+ export interface TablePropertyExceptions {
43
+ /** Border overrides for this row */
44
+ borders?: TableBorders;
45
+ /** Shading override for this row */
46
+ shading?: Shading;
47
+ /** Cell spacing override in twips */
48
+ cellSpacing?: number;
49
+ /** Table width override in twips */
50
+ width?: number;
51
+ /** Table indentation override in twips */
52
+ indentation?: number;
53
+ /** Table justification override */
54
+ justification?: RowJustification;
55
+ }
56
+
57
+ /**
58
+ * Row formatting options
59
+ */
60
+ export interface RowFormatting {
61
+ height?: number; // Height in twips
62
+ heightRule?: 'auto' | 'exact' | 'atLeast';
63
+ isHeader?: boolean; // Whether this is a header row
64
+ cantSplit?: boolean; // Prevent row from breaking across pages
65
+ justification?: RowJustification; // Row justification/alignment
66
+ hidden?: boolean; // Hide row
67
+ gridBefore?: number; // Grid columns before first cell
68
+ gridAfter?: number; // Grid columns after last cell
69
+ tablePropertyExceptions?: TablePropertyExceptions; // Table property exceptions for this row
70
+ wBefore?: number; // Width before row in twips (per ECMA-376 §17.4.83)
71
+ wBeforeType?: string; // Width before type (dxa, pct, auto)
72
+ wAfter?: number; // Width after row in twips (per ECMA-376 §17.4.82)
73
+ wAfterType?: string; // Width after type (dxa, pct, auto)
74
+ cellSpacing?: number; // Row-level cell spacing override in twips
75
+ cellSpacingType?: string; // Cell spacing type (dxa, pct)
76
+ cnfStyle?: string; // Conditional formatting bitmask (per ECMA-376 §17.3.1.8)
77
+ divId?: number; // HTML div association (per ECMA-376 §17.4.9)
78
+ }
79
+
80
+ /**
81
+ * Table row property change tracking (w:trPrChange)
82
+ * Per ECMA-376 Part 1 §17.13.5.38
83
+ */
84
+ export interface TrPrChange {
85
+ author: string;
86
+ date: string;
87
+ id: string;
88
+ previousProperties: Record<string, any>;
89
+ }
90
+
91
+ /**
92
+ * Represents a table row
93
+ */
94
+ export class TableRow {
95
+ private cells: TableCell[] = [];
96
+ private formatting: RowFormatting;
97
+ /** Parent table reference (if row is inside a table) */
98
+ private _parentTable?: import('./Table').Table;
99
+ /** Tracking context for automatic change tracking */
100
+ private trackingContext?: import('../tracking/TrackingContext').TrackingContext;
101
+ /** Table row property change tracking (w:trPrChange) */
102
+ private trPrChange?: TrPrChange;
103
+
104
+ /**
105
+ * Creates a new TableRow
106
+ * @param cellCount - Number of cells to create (optional)
107
+ * @param formatting - Row formatting options
108
+ */
109
+ constructor(cellCount?: number, formatting: RowFormatting = {}) {
110
+ this.formatting = formatting;
111
+
112
+ if (cellCount !== undefined && cellCount > 0) {
113
+ for (let i = 0; i < cellCount; i++) {
114
+ const cell = new TableCell();
115
+ cell._setParentRow(this);
116
+ this.cells.push(cell);
117
+ }
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Sets the tracking context for automatic change tracking.
123
+ * Called by Document when track changes is enabled.
124
+ * @internal
125
+ */
126
+ _setTrackingContext(context: import('../tracking/TrackingContext').TrackingContext): void {
127
+ this.trackingContext = context;
128
+ }
129
+
130
+ /**
131
+ * Gets the table row property change tracking info
132
+ */
133
+ getTrPrChange(): TrPrChange | undefined {
134
+ return this.trPrChange;
135
+ }
136
+
137
+ /**
138
+ * Sets the table row property change tracking info
139
+ */
140
+ setTrPrChange(change: TrPrChange | undefined): void {
141
+ this.trPrChange = change;
142
+ }
143
+
144
+ /**
145
+ * Clears the table row property change tracking
146
+ */
147
+ clearTrPrChange(): void {
148
+ this.trPrChange = undefined;
149
+ }
150
+
151
+ /**
152
+ * Adds a cell to the row
153
+ * @param cell - Cell to add
154
+ * @returns This row for chaining
155
+ */
156
+ addCell(cell: TableCell): this {
157
+ this.cells.push(cell);
158
+ cell._setParentRow(this);
159
+ return this;
160
+ }
161
+
162
+ /**
163
+ * Creates and adds a new cell
164
+ * @param text - Optional text content for the cell
165
+ * @returns The created cell
166
+ */
167
+ createCell(text?: string): TableCell {
168
+ const cell = new TableCell();
169
+ if (text) {
170
+ cell.createParagraph(text);
171
+ }
172
+ this.cells.push(cell);
173
+ cell._setParentRow(this);
174
+ return cell;
175
+ }
176
+
177
+ /**
178
+ * Gets a cell by index
179
+ * @param index - Cell index (0-based)
180
+ * @returns The cell at the index, or undefined
181
+ */
182
+ getCell(index: number): TableCell | undefined {
183
+ return this.cells[index];
184
+ }
185
+
186
+ /**
187
+ * Inserts a cell at the specified index
188
+ * @param index - Position to insert (0-based)
189
+ * @param cell - Cell to insert
190
+ */
191
+ insertCellAt(index: number, cell: TableCell): void {
192
+ this.cells.splice(index, 0, cell);
193
+ cell._setParentRow(this);
194
+ }
195
+
196
+ /**
197
+ * Removes and returns the cell at the specified index
198
+ * @param index - Position to remove (0-based)
199
+ * @returns The removed cell, or undefined if index is out of bounds
200
+ */
201
+ removeCellAt(index: number): TableCell | undefined {
202
+ if (index < 0 || index >= this.cells.length) return undefined;
203
+ const removed = this.cells.splice(index, 1);
204
+ const cell = removed[0];
205
+ if (cell) cell._setParentRow(undefined);
206
+ return cell;
207
+ }
208
+
209
+ /**
210
+ * Gets all cells in the row
211
+ * @returns Array of cells
212
+ */
213
+ getCells(): TableCell[] {
214
+ return [...this.cells];
215
+ }
216
+
217
+ /**
218
+ * Gets the number of cells in the row
219
+ * @returns Number of cells
220
+ */
221
+ getCellCount(): number {
222
+ return this.cells.length;
223
+ }
224
+
225
+ /**
226
+ * Calculates the total grid span of the row (considering column spans)
227
+ *
228
+ * For tables with merged cells, this returns the number of logical columns
229
+ * this row spans based on the columnSpan values of each cell.
230
+ *
231
+ * @returns Total grid span (sum of all cell spans, where unspanned cells count as 1)
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * // Row with 3 cells, middle one spanning 2 columns
236
+ * const row = new TableRow();
237
+ * row.createCell('A'); // span = 1
238
+ * row.createCell('B').setColumnSpan(2); // span = 2
239
+ * row.createCell('C'); // span = 1
240
+ * row.getTotalGridSpan(); // Returns 4
241
+ * ```
242
+ */
243
+ getTotalGridSpan(): number {
244
+ let totalSpan = 0;
245
+ for (const cell of this.cells) {
246
+ const formatting = cell.getFormatting();
247
+ totalSpan += formatting.columnSpan || 1;
248
+ }
249
+ return totalSpan;
250
+ }
251
+
252
+ /**
253
+ * Validates the row's grid alignment
254
+ *
255
+ * Checks if the total grid span matches the expected column count.
256
+ * Logs a warning if there's a mismatch, which can indicate:
257
+ * - Missing cells (grid span < expected)
258
+ * - Extra cells (grid span > expected)
259
+ * - Incorrect columnSpan values
260
+ *
261
+ * @param expectedColumns - Expected number of columns in the table grid
262
+ * @returns Object with validation result and details
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * const result = row.validateGridAlignment(4);
267
+ * if (!result.isValid) {
268
+ * console.log(result.message); // "Row has 3 grid columns but expected 4"
269
+ * }
270
+ * ```
271
+ */
272
+ validateGridAlignment(expectedColumns: number): {
273
+ isValid: boolean;
274
+ actualSpan: number;
275
+ message?: string;
276
+ } {
277
+ const actualSpan = this.getTotalGridSpan();
278
+
279
+ if (actualSpan === expectedColumns) {
280
+ return { isValid: true, actualSpan };
281
+ }
282
+
283
+ const message =
284
+ `Row grid alignment mismatch: has ${actualSpan} grid columns but expected ${expectedColumns}. ` +
285
+ `Cell count: ${this.cells.length}. ` +
286
+ (actualSpan < expectedColumns
287
+ ? 'Missing cells or incorrect columnSpan values.'
288
+ : 'Extra cells or excessive columnSpan values.');
289
+
290
+ defaultLogger.warn(`[TableRow] ${message}`);
291
+
292
+ return {
293
+ isValid: false,
294
+ actualSpan,
295
+ message,
296
+ };
297
+ }
298
+
299
+ /**
300
+ * Sets row height
301
+ * @param twips - Height in twips
302
+ * @param rule - Height rule
303
+ * @returns This row for chaining
304
+ */
305
+ setHeight(twips: number, rule: RowFormatting['heightRule'] = 'atLeast'): this {
306
+ const prevHeight = this.formatting.height;
307
+ const prevRule = this.formatting.heightRule;
308
+ this.formatting.height = twips;
309
+ this.formatting.heightRule = rule;
310
+ if (this.trackingContext?.isEnabled()) {
311
+ if (prevHeight !== twips) {
312
+ this.trackingContext.trackTableChange(this, 'height', prevHeight, twips);
313
+ }
314
+ if (prevRule !== rule) {
315
+ this.trackingContext.trackTableChange(this, 'heightRule', prevRule, rule);
316
+ }
317
+ }
318
+ return this;
319
+ }
320
+
321
+ /**
322
+ * Clears the row height, allowing Word to auto-size the row based on content
323
+ * @returns This row for chaining
324
+ */
325
+ clearHeight(): this {
326
+ delete this.formatting.height;
327
+ delete this.formatting.heightRule;
328
+ return this;
329
+ }
330
+
331
+ /**
332
+ * Sets whether this is a header row
333
+ * @param isHeader - Whether this is a header row
334
+ * @returns This row for chaining
335
+ */
336
+ setHeader(isHeader = true): this {
337
+ const prev = this.formatting.isHeader;
338
+ this.formatting.isHeader = isHeader;
339
+ if (this.trackingContext?.isEnabled() && prev !== isHeader) {
340
+ this.trackingContext.trackTableChange(this, 'isHeader', prev, isHeader);
341
+ }
342
+ return this;
343
+ }
344
+
345
+ /**
346
+ * Sets whether row can split across pages
347
+ * @param cantSplit - Whether to prevent splitting
348
+ * @returns This row for chaining
349
+ */
350
+ setCantSplit(cantSplit = true): this {
351
+ const prev = this.formatting.cantSplit;
352
+ this.formatting.cantSplit = cantSplit;
353
+ if (this.trackingContext?.isEnabled() && prev !== cantSplit) {
354
+ this.trackingContext.trackTableChange(this, 'cantSplit', prev, cantSplit);
355
+ }
356
+ return this;
357
+ }
358
+
359
+ /**
360
+ * Sets row justification/alignment
361
+ * Per ECMA-376 Part 1 §17.4.79 (w:jc)
362
+ * @param alignment - Row justification ('left' | 'center' | 'right' | 'start' | 'end')
363
+ * @returns This row for chaining
364
+ * @example
365
+ * ```typescript
366
+ * row.setJustification('center'); // Center-align the entire row
367
+ * ```
368
+ */
369
+ setJustification(alignment: RowJustification): this {
370
+ const prev = this.formatting.justification;
371
+ this.formatting.justification = alignment;
372
+ if (this.trackingContext?.isEnabled() && prev !== alignment) {
373
+ this.trackingContext.trackTableChange(this, 'justification', prev, alignment);
374
+ }
375
+ return this;
376
+ }
377
+
378
+ /**
379
+ * Sets whether row is hidden
380
+ * Per ECMA-376 Part 1 §17.4.23 (w:hidden)
381
+ * @param hidden - Whether to hide the row
382
+ * @returns This row for chaining
383
+ * @example
384
+ * ```typescript
385
+ * row.setHidden(true); // Hide this row from display
386
+ * ```
387
+ */
388
+ setHidden(hidden = true): this {
389
+ const prev = this.formatting.hidden;
390
+ this.formatting.hidden = hidden;
391
+ if (this.trackingContext?.isEnabled() && prev !== hidden) {
392
+ this.trackingContext.trackTableChange(this, 'hidden', prev, hidden);
393
+ }
394
+ return this;
395
+ }
396
+
397
+ /**
398
+ * Sets grid columns before first cell
399
+ * Per ECMA-376 Part 1 §17.4.15 (w:gridBefore)
400
+ * Specifies number of grid columns that must be skipped before the first cell
401
+ * @param columns - Number of grid columns to skip before first cell
402
+ * @returns This row for chaining
403
+ * @example
404
+ * ```typescript
405
+ * row.setGridBefore(2); // Skip 2 columns before first cell
406
+ * ```
407
+ */
408
+ setGridBefore(columns: number): this {
409
+ const prev = this.formatting.gridBefore;
410
+ this.formatting.gridBefore = columns;
411
+ if (this.trackingContext?.isEnabled() && prev !== columns) {
412
+ this.trackingContext.trackTableChange(this, 'gridBefore', prev, columns);
413
+ }
414
+ return this;
415
+ }
416
+
417
+ /**
418
+ * Sets grid columns after last cell
419
+ * Per ECMA-376 Part 1 §17.4.14 (w:gridAfter)
420
+ * Specifies number of grid columns that must be left after the last cell
421
+ * @param columns - Number of grid columns to leave after last cell
422
+ * @returns This row for chaining
423
+ * @example
424
+ * ```typescript
425
+ * row.setGridAfter(1); // Leave 1 column after last cell
426
+ * ```
427
+ */
428
+ setGridAfter(columns: number): this {
429
+ const prev = this.formatting.gridAfter;
430
+ this.formatting.gridAfter = columns;
431
+ if (this.trackingContext?.isEnabled() && prev !== columns) {
432
+ this.trackingContext.trackTableChange(this, 'gridAfter', prev, columns);
433
+ }
434
+ return this;
435
+ }
436
+
437
+ /**
438
+ * Sets table property exceptions for this row
439
+ * Per ECMA-376 Part 1 §17.4.61 (w:tblPrEx)
440
+ *
441
+ * Allows this row to override table-level properties like borders, shading, and cell spacing.
442
+ * Typically used when merging tables or preserving formatting from legacy documents.
443
+ *
444
+ * @param exceptions - Table property exceptions configuration
445
+ * @returns This row for chaining
446
+ * @example
447
+ * ```typescript
448
+ * // Override borders for this row
449
+ * row.setTablePropertyExceptions({
450
+ * borders: {
451
+ * top: { style: 'single', size: 8, color: 'FF0000' },
452
+ * bottom: { style: 'single', size: 8, color: 'FF0000' }
453
+ * },
454
+ * shading: { fill: 'FFFF00', pattern: 'clear' }
455
+ * });
456
+ * ```
457
+ */
458
+ setTablePropertyExceptions(exceptions: TablePropertyExceptions): this {
459
+ const prev = this.formatting.tablePropertyExceptions;
460
+ this.formatting.tablePropertyExceptions = exceptions;
461
+ if (this.trackingContext?.isEnabled() && prev !== exceptions) {
462
+ this.trackingContext.trackTableChange(this, 'tablePropertyExceptions', prev, exceptions);
463
+ }
464
+ return this;
465
+ }
466
+
467
+ /**
468
+ * Sets width before row (w:wBefore) per ECMA-376 Part 1 §17.4.83
469
+ * @param width - Width in twips
470
+ * @param type - Width type (dxa, pct, auto)
471
+ * @returns This row for chaining
472
+ */
473
+ setWBefore(width: number, type = 'dxa'): this {
474
+ const prevWidth = this.formatting.wBefore;
475
+ const prevType = this.formatting.wBeforeType;
476
+ this.formatting.wBefore = width;
477
+ this.formatting.wBeforeType = type;
478
+ if (this.trackingContext?.isEnabled()) {
479
+ if (prevWidth !== width) {
480
+ this.trackingContext.trackTableChange(this, 'wBefore', prevWidth, width);
481
+ }
482
+ if (prevType !== type) {
483
+ this.trackingContext.trackTableChange(this, 'wBeforeType', prevType, type);
484
+ }
485
+ }
486
+ return this;
487
+ }
488
+
489
+ /**
490
+ * Sets width after row (w:wAfter) per ECMA-376 Part 1 §17.4.82
491
+ * @param width - Width in twips
492
+ * @param type - Width type (dxa, pct, auto)
493
+ * @returns This row for chaining
494
+ */
495
+ setWAfter(width: number, type = 'dxa'): this {
496
+ const prevWidth = this.formatting.wAfter;
497
+ const prevType = this.formatting.wAfterType;
498
+ this.formatting.wAfter = width;
499
+ this.formatting.wAfterType = type;
500
+ if (this.trackingContext?.isEnabled()) {
501
+ if (prevWidth !== width) {
502
+ this.trackingContext.trackTableChange(this, 'wAfter', prevWidth, width);
503
+ }
504
+ if (prevType !== type) {
505
+ this.trackingContext.trackTableChange(this, 'wAfterType', prevType, type);
506
+ }
507
+ }
508
+ return this;
509
+ }
510
+
511
+ /**
512
+ * Sets row-level cell spacing override (w:tblCellSpacing on row)
513
+ * @param spacing - Cell spacing in twips
514
+ * @param type - Spacing type (dxa, pct)
515
+ * @returns This row for chaining
516
+ */
517
+ setRowCellSpacing(spacing: number, type = 'dxa'): this {
518
+ const prevSpacing = this.formatting.cellSpacing;
519
+ const prevType = this.formatting.cellSpacingType;
520
+ this.formatting.cellSpacing = spacing;
521
+ this.formatting.cellSpacingType = type;
522
+ if (this.trackingContext?.isEnabled()) {
523
+ if (prevSpacing !== spacing) {
524
+ this.trackingContext.trackTableChange(this, 'cellSpacing', prevSpacing, spacing);
525
+ }
526
+ if (prevType !== type) {
527
+ this.trackingContext.trackTableChange(this, 'cellSpacingType', prevType, type);
528
+ }
529
+ }
530
+ return this;
531
+ }
532
+
533
+ /**
534
+ * Sets conditional formatting bitmask for this row (w:cnfStyle)
535
+ * Per ECMA-376 Part 1 §17.3.1.8
536
+ * @param cnfStyle - Binary string (e.g., '100000000000' for firstRow)
537
+ * @returns This row for chaining
538
+ */
539
+ setCnfStyle(cnfStyle: string): this {
540
+ const prev = this.formatting.cnfStyle;
541
+ this.formatting.cnfStyle = cnfStyle;
542
+ if (this.trackingContext?.isEnabled() && prev !== cnfStyle) {
543
+ this.trackingContext.trackTableChange(this, 'cnfStyle', prev, cnfStyle);
544
+ }
545
+ return this;
546
+ }
547
+
548
+ /**
549
+ * Sets the HTML div ID for web round-trip
550
+ * Per ECMA-376 Part 1 §17.4.9
551
+ * @param id - Div ID number
552
+ * @returns This row for chaining
553
+ */
554
+ setDivId(id: number): this {
555
+ this.formatting.divId = id;
556
+ return this;
557
+ }
558
+
559
+ /**
560
+ * Gets the HTML div ID
561
+ * @returns Div ID or undefined
562
+ */
563
+ getDivId(): number | undefined {
564
+ return this.formatting.divId;
565
+ }
566
+
567
+ /**
568
+ * Gets table property exceptions
569
+ * @returns Table property exceptions or undefined
570
+ */
571
+ getTablePropertyExceptions(): TablePropertyExceptions | undefined {
572
+ return this.formatting.tablePropertyExceptions;
573
+ }
574
+
575
+ /**
576
+ * Gets the row formatting
577
+ * @returns Row formatting
578
+ */
579
+ getFormatting(): RowFormatting {
580
+ return { ...this.formatting };
581
+ }
582
+
583
+ // ============================================================================
584
+ // Individual Formatting Getters
585
+ // ============================================================================
586
+
587
+ /**
588
+ * Gets the row height in twips
589
+ * @returns Height in twips or undefined if not set
590
+ */
591
+ getHeight(): number | undefined {
592
+ return this.formatting.height;
593
+ }
594
+
595
+ /**
596
+ * Gets the row height rule
597
+ * @returns Height rule ('auto', 'exact', 'atLeast') or undefined
598
+ */
599
+ getHeightRule(): string | undefined {
600
+ return this.formatting.heightRule;
601
+ }
602
+
603
+ /**
604
+ * Checks if this row is marked as a header row
605
+ * @returns True if this is a header row
606
+ */
607
+ getIsHeader(): boolean {
608
+ return this.formatting.isHeader ?? false;
609
+ }
610
+
611
+ /**
612
+ * Gets whether the row can split across pages
613
+ * @returns True if row cannot split
614
+ */
615
+ getCantSplit(): boolean {
616
+ return this.formatting.cantSplit ?? false;
617
+ }
618
+
619
+ /**
620
+ * Gets the row justification (alignment)
621
+ * @returns Justification ('left', 'center', 'right') or undefined
622
+ */
623
+ getJustification(): string | undefined {
624
+ return this.formatting.justification;
625
+ }
626
+
627
+ /**
628
+ * Checks if this row is hidden
629
+ * @returns True if row is hidden
630
+ */
631
+ isHidden(): boolean {
632
+ return this.formatting.hidden ?? false;
633
+ }
634
+
635
+ /**
636
+ * Sets the parent table reference for this row.
637
+ * Called by Table when adding rows.
638
+ * @internal
639
+ */
640
+ _setParentTable(table: import('./Table').Table | undefined): void {
641
+ this._parentTable = table;
642
+ }
643
+
644
+ /**
645
+ * Gets the parent table reference for this row.
646
+ * @internal
647
+ */
648
+ _getParentTable(): import('./Table').Table | undefined {
649
+ return this._parentTable;
650
+ }
651
+
652
+ /**
653
+ * Builds XML for table property exceptions
654
+ * Per ECMA-376 Part 1 §17.4.61
655
+ * @private
656
+ */
657
+ private buildTablePropertyExceptionsXML(exceptions: TablePropertyExceptions): XMLElement[] {
658
+ const children: XMLElement[] = [];
659
+
660
+ // Add table width exception (w:tblW)
661
+ if (exceptions.width !== undefined) {
662
+ children.push(
663
+ XMLBuilder.wSelf('tblW', {
664
+ 'w:w': exceptions.width,
665
+ 'w:type': 'dxa',
666
+ })
667
+ );
668
+ }
669
+
670
+ // Add table justification exception (w:jc)
671
+ if (exceptions.justification) {
672
+ children.push(XMLBuilder.wSelf('jc', { 'w:val': exceptions.justification }));
673
+ }
674
+
675
+ // Add cell spacing exception (w:tblCellSpacing)
676
+ if (exceptions.cellSpacing !== undefined) {
677
+ children.push(
678
+ XMLBuilder.wSelf('tblCellSpacing', {
679
+ 'w:w': exceptions.cellSpacing,
680
+ 'w:type': 'dxa',
681
+ })
682
+ );
683
+ }
684
+
685
+ // Add table indentation exception (w:tblInd)
686
+ if (exceptions.indentation !== undefined) {
687
+ children.push(
688
+ XMLBuilder.wSelf('tblInd', {
689
+ 'w:w': exceptions.indentation,
690
+ 'w:type': 'dxa',
691
+ })
692
+ );
693
+ }
694
+
695
+ // Add table borders exception (w:tblBorders)
696
+ if (exceptions.borders) {
697
+ const borderChildren = this.buildBordersXML(exceptions.borders);
698
+ if (borderChildren.length > 0) {
699
+ children.push(XMLBuilder.w('tblBorders', undefined, borderChildren));
700
+ }
701
+ }
702
+
703
+ // Add shading exception (w:shd)
704
+ if (exceptions.shading) {
705
+ const shdAttrs: Record<string, string> = {};
706
+ if (exceptions.shading.pattern) shdAttrs['w:val'] = exceptions.shading.pattern;
707
+ if (exceptions.shading.color) shdAttrs['w:color'] = exceptions.shading.color;
708
+ if (exceptions.shading.fill) shdAttrs['w:fill'] = exceptions.shading.fill;
709
+ if (exceptions.shading.themeColor) shdAttrs['w:themeColor'] = exceptions.shading.themeColor;
710
+ if (exceptions.shading.themeFill) shdAttrs['w:themeFill'] = exceptions.shading.themeFill;
711
+ if (exceptions.shading.themeFillShade)
712
+ shdAttrs['w:themeFillShade'] = exceptions.shading.themeFillShade;
713
+ if (exceptions.shading.themeFillTint)
714
+ shdAttrs['w:themeFillTint'] = exceptions.shading.themeFillTint;
715
+ if (exceptions.shading.themeShade) shdAttrs['w:themeShade'] = exceptions.shading.themeShade;
716
+ if (exceptions.shading.themeTint) shdAttrs['w:themeTint'] = exceptions.shading.themeTint;
717
+ children.push(XMLBuilder.w('shd', shdAttrs));
718
+ }
719
+
720
+ return children;
721
+ }
722
+
723
+ /**
724
+ * Builds XML for table borders
725
+ * @private
726
+ */
727
+ private buildBordersXML(borders: TableBorders): XMLElement[] {
728
+ const children: XMLElement[] = [];
729
+
730
+ const borderNames: (keyof TableBorders)[] = [
731
+ 'top',
732
+ 'left',
733
+ 'bottom',
734
+ 'right',
735
+ 'insideH',
736
+ 'insideV',
737
+ ];
738
+
739
+ for (const name of borderNames) {
740
+ const border = borders[name];
741
+ if (border) {
742
+ const attrs: Record<string, string | number> = {};
743
+ if (border.style) attrs['w:val'] = border.style;
744
+ if (border.size !== undefined) attrs['w:sz'] = border.size;
745
+ if (border.space !== undefined) attrs['w:space'] = border.space;
746
+ if (border.color) attrs['w:color'] = border.color;
747
+
748
+ if (Object.keys(attrs).length > 0) {
749
+ children.push(XMLBuilder.wSelf(name, attrs));
750
+ }
751
+ }
752
+ }
753
+
754
+ return children;
755
+ }
756
+
757
+ /**
758
+ * Converts the row to WordprocessingML XML element
759
+ * @returns XMLElement representing the row
760
+ */
761
+ toXML(): XMLElement {
762
+ const trPrChildren: XMLElement[] = [];
763
+
764
+ // Ordered per CT_TrPr (ECMA-376 §17.4.79):
765
+ // cnfStyle divId → gridBefore → gridAfter → wBefore → wAfter →
766
+ // cantSplit → trHeight → tblHeader → tblCellSpacing → jc → hidden
767
+
768
+ // 1. cnfStyle (conditional formatting bitmask)
769
+ if (this.formatting.cnfStyle) {
770
+ trPrChildren.push(XMLBuilder.wSelf('cnfStyle', { 'w:val': this.formatting.cnfStyle }));
771
+ }
772
+
773
+ // 2. divId
774
+ if (this.formatting.divId !== undefined) {
775
+ trPrChildren.push(XMLBuilder.wSelf('divId', { 'w:val': this.formatting.divId }));
776
+ }
777
+
778
+ // 3. gridBefore
779
+ if (this.formatting.gridBefore !== undefined) {
780
+ trPrChildren.push(XMLBuilder.wSelf('gridBefore', { 'w:val': this.formatting.gridBefore }));
781
+ }
782
+
783
+ // 4. gridAfter
784
+ if (this.formatting.gridAfter !== undefined) {
785
+ trPrChildren.push(XMLBuilder.wSelf('gridAfter', { 'w:val': this.formatting.gridAfter }));
786
+ }
787
+
788
+ // 5. wBefore
789
+ if (this.formatting.wBefore !== undefined) {
790
+ trPrChildren.push(
791
+ XMLBuilder.wSelf('wBefore', {
792
+ 'w:w': this.formatting.wBefore,
793
+ 'w:type': this.formatting.wBeforeType || 'dxa',
794
+ })
795
+ );
796
+ }
797
+
798
+ // 6. wAfter
799
+ if (this.formatting.wAfter !== undefined) {
800
+ trPrChildren.push(
801
+ XMLBuilder.wSelf('wAfter', {
802
+ 'w:w': this.formatting.wAfter,
803
+ 'w:type': this.formatting.wAfterType || 'dxa',
804
+ })
805
+ );
806
+ }
807
+
808
+ // 7. cantSplit
809
+ if (this.formatting.cantSplit) {
810
+ trPrChildren.push(XMLBuilder.wSelf('cantSplit'));
811
+ }
812
+
813
+ // 8. trHeight
814
+ if (this.formatting.height !== undefined) {
815
+ const attrs: Record<string, string | number> = {
816
+ 'w:val': this.formatting.height,
817
+ };
818
+ if (this.formatting.heightRule) {
819
+ attrs['w:hRule'] = this.formatting.heightRule;
820
+ }
821
+ trPrChildren.push(XMLBuilder.wSelf('trHeight', attrs));
822
+ }
823
+
824
+ // 9. tblHeader
825
+ if (this.formatting.isHeader) {
826
+ trPrChildren.push(XMLBuilder.wSelf('tblHeader'));
827
+ }
828
+
829
+ // 10. tblCellSpacing
830
+ if (this.formatting.cellSpacing !== undefined) {
831
+ trPrChildren.push(
832
+ XMLBuilder.wSelf('tblCellSpacing', {
833
+ 'w:w': this.formatting.cellSpacing,
834
+ 'w:type': this.formatting.cellSpacingType || 'dxa',
835
+ })
836
+ );
837
+ }
838
+
839
+ // 11. jc (map 'start'/'end' to valid ST_JcTable values)
840
+ if (this.formatting.justification) {
841
+ const jcMap: Record<string, string> = { start: 'left', end: 'right' };
842
+ const jcVal = jcMap[this.formatting.justification] || this.formatting.justification;
843
+ trPrChildren.push(XMLBuilder.wSelf('jc', { 'w:val': jcVal }));
844
+ }
845
+
846
+ // 12. hidden
847
+ if (this.formatting.hidden) {
848
+ trPrChildren.push(XMLBuilder.wSelf('hidden'));
849
+ }
850
+
851
+ // Add table row property change (w:trPrChange) per ECMA-376 Part 1 §17.13.5.38
852
+ // Must be last child of w:trPr
853
+ if (this.trPrChange) {
854
+ const changeAttrs: Record<string, string | number> = {
855
+ 'w:id': this.trPrChange.id,
856
+ 'w:author': this.trPrChange.author,
857
+ 'w:date': this.trPrChange.date,
858
+ };
859
+ const prevTrPrChildren: XMLElement[] = [];
860
+ const prev = this.trPrChange.previousProperties;
861
+ if (prev) {
862
+ // Ordered per CT_TrPr: cnfStyle → gridBefore → gridAfter → wBefore → wAfter →
863
+ // cantSplit → trHeight → tblHeader → tblCellSpacing → jc → hidden
864
+ if (prev.cnfStyle) {
865
+ prevTrPrChildren.push(XMLBuilder.wSelf('cnfStyle', { 'w:val': prev.cnfStyle }));
866
+ }
867
+ if (prev.gridBefore !== undefined) {
868
+ prevTrPrChildren.push(XMLBuilder.wSelf('gridBefore', { 'w:val': prev.gridBefore }));
869
+ }
870
+ if (prev.gridAfter !== undefined) {
871
+ prevTrPrChildren.push(XMLBuilder.wSelf('gridAfter', { 'w:val': prev.gridAfter }));
872
+ }
873
+ if (prev.wBefore !== undefined) {
874
+ prevTrPrChildren.push(
875
+ XMLBuilder.wSelf('wBefore', {
876
+ 'w:w': prev.wBefore,
877
+ 'w:type': prev.wBeforeType || 'dxa',
878
+ })
879
+ );
880
+ }
881
+ if (prev.wAfter !== undefined) {
882
+ prevTrPrChildren.push(
883
+ XMLBuilder.wSelf('wAfter', {
884
+ 'w:w': prev.wAfter,
885
+ 'w:type': prev.wAfterType || 'dxa',
886
+ })
887
+ );
888
+ }
889
+ if (prev.cantSplit) {
890
+ prevTrPrChildren.push(XMLBuilder.wSelf('cantSplit'));
891
+ }
892
+ if (prev.height !== undefined) {
893
+ const heightAttrs: Record<string, string | number> = { 'w:val': prev.height };
894
+ if (prev.heightRule) heightAttrs['w:hRule'] = prev.heightRule;
895
+ prevTrPrChildren.push(XMLBuilder.wSelf('trHeight', heightAttrs));
896
+ }
897
+ if (prev.isHeader) {
898
+ prevTrPrChildren.push(XMLBuilder.wSelf('tblHeader'));
899
+ }
900
+ if (prev.cellSpacing !== undefined) {
901
+ prevTrPrChildren.push(
902
+ XMLBuilder.wSelf('tblCellSpacing', {
903
+ 'w:w': prev.cellSpacing,
904
+ 'w:type': prev.cellSpacingType || 'dxa',
905
+ })
906
+ );
907
+ }
908
+ if (prev.justification) {
909
+ const jcPrevMap: Record<string, string> = { start: 'left', end: 'right' };
910
+ prevTrPrChildren.push(
911
+ XMLBuilder.wSelf('jc', { 'w:val': jcPrevMap[prev.justification] || prev.justification })
912
+ );
913
+ }
914
+ if (prev.hidden) {
915
+ prevTrPrChildren.push(XMLBuilder.wSelf('hidden'));
916
+ }
917
+ }
918
+ const prevTrPr = XMLBuilder.w('trPr', undefined, prevTrPrChildren);
919
+ trPrChildren.push(XMLBuilder.w('trPrChange', changeAttrs, [prevTrPr]));
920
+ }
921
+
922
+ // Build row element
923
+ const rowChildren: XMLElement[] = [];
924
+
925
+ // Add row properties if there are any
926
+ if (trPrChildren.length > 0) {
927
+ rowChildren.push(XMLBuilder.w('trPr', undefined, trPrChildren));
928
+ }
929
+
930
+ // Add table property exceptions (tblPrEx) if present
931
+ if (this.formatting.tablePropertyExceptions) {
932
+ const tblPrExChildren = this.buildTablePropertyExceptionsXML(
933
+ this.formatting.tablePropertyExceptions
934
+ );
935
+ if (tblPrExChildren.length > 0) {
936
+ rowChildren.push(XMLBuilder.w('tblPrEx', undefined, tblPrExChildren));
937
+ }
938
+ }
939
+
940
+ // Add all cells - each cell is independent
941
+ // Note: gridSpan (columnSpan) means a single cell spans multiple columns in the grid,
942
+ // it does NOT mean subsequent cells should be skipped. Each cell in the array
943
+ // represents a distinct cell that should be output to the XML.
944
+ for (const cell of this.cells) {
945
+ rowChildren.push(cell.toXML());
946
+ }
947
+
948
+ return XMLBuilder.w('tr', undefined, rowChildren);
949
+ }
950
+
951
+ /**
952
+ * Creates a new TableRow
953
+ * @param cellCount - Number of cells to create
954
+ * @param formatting - Row formatting
955
+ * @returns New TableRow instance
956
+ */
957
+ static create(cellCount?: number, formatting?: RowFormatting): TableRow {
958
+ return new TableRow(cellCount, formatting);
959
+ }
960
+ }