docxmlater 10.1.4 → 10.1.6

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 (372) 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 +51 -50
  6. package/dist/core/Document.d.ts.map +1 -1
  7. package/dist/core/Document.js +486 -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 +1 -0
  146. package/dist/formatting/NumberingManager.d.ts.map +1 -1
  147. package/dist/formatting/NumberingManager.js +27 -9
  148. package/dist/formatting/NumberingManager.js.map +1 -1
  149. package/dist/formatting/Style.d.ts +11 -11
  150. package/dist/formatting/Style.d.ts.map +1 -1
  151. package/dist/formatting/Style.js +219 -247
  152. package/dist/formatting/Style.js.map +1 -1
  153. package/dist/formatting/StylesManager.d.ts +2 -2
  154. package/dist/formatting/StylesManager.d.ts.map +1 -1
  155. package/dist/formatting/StylesManager.js +96 -102
  156. package/dist/formatting/StylesManager.js.map +1 -1
  157. package/dist/helpers/CleanupHelper.d.ts +1 -1
  158. package/dist/helpers/CleanupHelper.d.ts.map +1 -1
  159. package/dist/helpers/CleanupHelper.js +6 -6
  160. package/dist/helpers/CleanupHelper.js.map +1 -1
  161. package/dist/images/ImageOptimizer.js +7 -7
  162. package/dist/images/ImageOptimizer.js.map +1 -1
  163. package/dist/index.d.ts +9 -9
  164. package/dist/index.d.ts.map +1 -1
  165. package/dist/index.js.map +1 -1
  166. package/dist/managers/DrawingManager.js.map +1 -1
  167. package/dist/tracking/DocumentTrackingContext.d.ts.map +1 -1
  168. package/dist/tracking/DocumentTrackingContext.js +23 -7
  169. package/dist/tracking/DocumentTrackingContext.js.map +1 -1
  170. package/dist/tracking/TrackingContext.d.ts.map +1 -1
  171. package/dist/tracking/TrackingContext.js.map +1 -1
  172. package/dist/types/compatibility-types.js.map +1 -1
  173. package/dist/types/formatting.js.map +1 -1
  174. package/dist/types/list-types.d.ts +6 -6
  175. package/dist/types/list-types.js.map +1 -1
  176. package/dist/types/settings-types.js.map +1 -1
  177. package/dist/types/styleConfig.d.ts +2 -2
  178. package/dist/types/styleConfig.js.map +1 -1
  179. package/dist/utils/ChangelogGenerator.d.ts.map +1 -1
  180. package/dist/utils/ChangelogGenerator.js +97 -101
  181. package/dist/utils/ChangelogGenerator.js.map +1 -1
  182. package/dist/utils/CompatibilityUpgrader.d.ts.map +1 -1
  183. package/dist/utils/CompatibilityUpgrader.js +1 -1
  184. package/dist/utils/CompatibilityUpgrader.js.map +1 -1
  185. package/dist/utils/InMemoryRevisionAcceptor.d.ts.map +1 -1
  186. package/dist/utils/InMemoryRevisionAcceptor.js +1 -6
  187. package/dist/utils/InMemoryRevisionAcceptor.js.map +1 -1
  188. package/dist/utils/MoveOperationHelper.d.ts.map +1 -1
  189. package/dist/utils/MoveOperationHelper.js +1 -1
  190. package/dist/utils/MoveOperationHelper.js.map +1 -1
  191. package/dist/utils/RevisionAwareProcessor.d.ts.map +1 -1
  192. package/dist/utils/RevisionAwareProcessor.js +2 -4
  193. package/dist/utils/RevisionAwareProcessor.js.map +1 -1
  194. package/dist/utils/RevisionWalker.d.ts.map +1 -1
  195. package/dist/utils/RevisionWalker.js +4 -12
  196. package/dist/utils/RevisionWalker.js.map +1 -1
  197. package/dist/utils/SelectiveRevisionAcceptor.d.ts.map +1 -1
  198. package/dist/utils/SelectiveRevisionAcceptor.js +2 -6
  199. package/dist/utils/SelectiveRevisionAcceptor.js.map +1 -1
  200. package/dist/utils/ShadingResolver.d.ts.map +1 -1
  201. package/dist/utils/ShadingResolver.js +1 -1
  202. package/dist/utils/ShadingResolver.js.map +1 -1
  203. package/dist/utils/acceptRevisions.d.ts.map +1 -1
  204. package/dist/utils/acceptRevisions.js +23 -12
  205. package/dist/utils/acceptRevisions.js.map +1 -1
  206. package/dist/utils/cnfStyleDecoder.d.ts +1 -1
  207. package/dist/utils/cnfStyleDecoder.d.ts.map +1 -1
  208. package/dist/utils/cnfStyleDecoder.js +40 -40
  209. package/dist/utils/cnfStyleDecoder.js.map +1 -1
  210. package/dist/utils/corruptionDetection.d.ts.map +1 -1
  211. package/dist/utils/corruptionDetection.js.map +1 -1
  212. package/dist/utils/dateFormatting.js.map +1 -1
  213. package/dist/utils/deepClone.js +1 -1
  214. package/dist/utils/deepClone.js.map +1 -1
  215. package/dist/utils/diagnostics.d.ts.map +1 -1
  216. package/dist/utils/diagnostics.js +1 -1
  217. package/dist/utils/diagnostics.js.map +1 -1
  218. package/dist/utils/errorHandling.js.map +1 -1
  219. package/dist/utils/formatting.d.ts.map +1 -1
  220. package/dist/utils/formatting.js +10 -2
  221. package/dist/utils/formatting.js.map +1 -1
  222. package/dist/utils/list-detection.d.ts +2 -2
  223. package/dist/utils/list-detection.d.ts.map +1 -1
  224. package/dist/utils/list-detection.js +21 -23
  225. package/dist/utils/list-detection.js.map +1 -1
  226. package/dist/utils/logger.d.ts.map +1 -1
  227. package/dist/utils/logger.js +12 -7
  228. package/dist/utils/logger.js.map +1 -1
  229. package/dist/utils/parsingHelpers.js.map +1 -1
  230. package/dist/utils/stripTrackedChanges.d.ts.map +1 -1
  231. package/dist/utils/stripTrackedChanges.js +3 -3
  232. package/dist/utils/stripTrackedChanges.js.map +1 -1
  233. package/dist/utils/textDiff.d.ts +1 -1
  234. package/dist/utils/textDiff.js +8 -8
  235. package/dist/utils/textDiff.js.map +1 -1
  236. package/dist/utils/units.js.map +1 -1
  237. package/dist/utils/validation.d.ts.map +1 -1
  238. package/dist/utils/validation.js +24 -7
  239. package/dist/utils/validation.js.map +1 -1
  240. package/dist/utils/xmlSanitization.d.ts.map +1 -1
  241. package/dist/utils/xmlSanitization.js +3 -3
  242. package/dist/utils/xmlSanitization.js.map +1 -1
  243. package/dist/validation/RevisionAutoFixer.d.ts.map +1 -1
  244. package/dist/validation/RevisionAutoFixer.js +5 -5
  245. package/dist/validation/RevisionAutoFixer.js.map +1 -1
  246. package/dist/validation/RevisionValidator.d.ts.map +1 -1
  247. package/dist/validation/RevisionValidator.js +7 -9
  248. package/dist/validation/RevisionValidator.js.map +1 -1
  249. package/dist/validation/ValidationRules.js +3 -3
  250. package/dist/validation/ValidationRules.js.map +1 -1
  251. package/dist/validation/index.js.map +1 -1
  252. package/dist/xml/XMLBuilder.d.ts +1 -1
  253. package/dist/xml/XMLBuilder.d.ts.map +1 -1
  254. package/dist/xml/XMLBuilder.js +98 -100
  255. package/dist/xml/XMLBuilder.js.map +1 -1
  256. package/dist/xml/XMLParser.d.ts.map +1 -1
  257. package/dist/xml/XMLParser.js +61 -66
  258. package/dist/xml/XMLParser.js.map +1 -1
  259. package/dist/zip/ZipHandler.d.ts.map +1 -1
  260. package/dist/zip/ZipHandler.js.map +1 -1
  261. package/dist/zip/ZipReader.d.ts.map +1 -1
  262. package/dist/zip/ZipReader.js +1 -3
  263. package/dist/zip/ZipReader.js.map +1 -1
  264. package/dist/zip/ZipWriter.d.ts +1 -1
  265. package/dist/zip/ZipWriter.d.ts.map +1 -1
  266. package/dist/zip/ZipWriter.js +28 -36
  267. package/dist/zip/ZipWriter.js.map +1 -1
  268. package/dist/zip/types.js +1 -1
  269. package/dist/zip/types.js.map +1 -1
  270. package/package.json +92 -92
  271. package/src/__tests__/helper-methods.test.ts +512 -512
  272. package/src/constants/legacyCompatFlags.ts +138 -138
  273. package/src/constants/limits.ts +50 -50
  274. package/src/core/Document.ts +1010 -1145
  275. package/src/core/DocumentContent.ts +461 -467
  276. package/src/core/DocumentGenerator.ts +1133 -1104
  277. package/src/core/DocumentIdManager.ts +158 -158
  278. package/src/core/DocumentParser.ts +2347 -2716
  279. package/src/core/DocumentValidator.ts +363 -372
  280. package/src/core/Relationship.ts +367 -367
  281. package/src/core/RelationshipManager.ts +429 -428
  282. package/src/elements/AlternateContent.ts +42 -42
  283. package/src/elements/Bookmark.ts +212 -210
  284. package/src/elements/BookmarkManager.ts +247 -250
  285. package/src/elements/Comment.ts +356 -359
  286. package/src/elements/CommentManager.ts +499 -502
  287. package/src/elements/CommonTypes.ts +524 -549
  288. package/src/elements/CustomXml.ts +36 -36
  289. package/src/elements/Endnote.ts +221 -217
  290. package/src/elements/EndnoteManager.ts +246 -249
  291. package/src/elements/Field.ts +1292 -1233
  292. package/src/elements/FieldHelpers.ts +329 -333
  293. package/src/elements/FontManager.ts +336 -339
  294. package/src/elements/Footer.ts +269 -269
  295. package/src/elements/Footnote.ts +221 -217
  296. package/src/elements/FootnoteManager.ts +246 -249
  297. package/src/elements/Header.ts +269 -269
  298. package/src/elements/HeaderFooterManager.ts +219 -219
  299. package/src/elements/Hyperlink.ts +1288 -1193
  300. package/src/elements/Image.ts +1982 -1756
  301. package/src/elements/ImageManager.ts +437 -432
  302. package/src/elements/ImageRun.ts +59 -59
  303. package/src/elements/MathElement.ts +65 -65
  304. package/src/elements/Paragraph.ts +4347 -4287
  305. package/src/elements/PreservedElement.ts +53 -53
  306. package/src/elements/PropertyChangeTypes.ts +458 -442
  307. package/src/elements/RangeMarker.ts +382 -400
  308. package/src/elements/Revision.ts +1198 -1217
  309. package/src/elements/RevisionContent.ts +73 -73
  310. package/src/elements/RevisionManager.ts +1070 -1070
  311. package/src/elements/Run.ts +3103 -3073
  312. package/src/elements/Section.ts +1521 -1421
  313. package/src/elements/Shape.ts +884 -873
  314. package/src/elements/StructuredDocumentTag.ts +1176 -1207
  315. package/src/elements/Table.ts +2468 -2524
  316. package/src/elements/TableCell.ts +1617 -1621
  317. package/src/elements/TableGridChange.ts +149 -151
  318. package/src/elements/TableOfContents.ts +701 -691
  319. package/src/elements/TableOfContentsElement.ts +89 -89
  320. package/src/elements/TableRow.ts +960 -929
  321. package/src/elements/TextBox.ts +766 -768
  322. package/src/formatting/AbstractNumbering.ts +580 -579
  323. package/src/formatting/NumberingInstance.ts +295 -299
  324. package/src/formatting/NumberingLevel.ts +981 -1040
  325. package/src/formatting/NumberingManager.ts +875 -827
  326. package/src/formatting/Style.ts +1785 -1879
  327. package/src/formatting/StylesManager.ts +1090 -1130
  328. package/src/helpers/CleanupHelper.ts +524 -524
  329. package/src/images/ImageOptimizer.ts +274 -274
  330. package/src/index.ts +559 -554
  331. package/src/managers/DrawingManager.ts +319 -319
  332. package/src/tracking/DocumentTrackingContext.ts +687 -674
  333. package/src/tracking/TrackingContext.ts +175 -173
  334. package/src/types/compatibility-types.ts +49 -49
  335. package/src/types/formatting.ts +210 -210
  336. package/src/types/list-types.ts +14 -14
  337. package/src/types/settings-types.ts +59 -59
  338. package/src/types/styleConfig.ts +189 -189
  339. package/src/utils/ChangelogGenerator.ts +1583 -1581
  340. package/src/utils/CompatibilityUpgrader.ts +235 -237
  341. package/src/utils/InMemoryRevisionAcceptor.ts +691 -696
  342. package/src/utils/MoveOperationHelper.ts +233 -238
  343. package/src/utils/RevisionAwareProcessor.ts +518 -526
  344. package/src/utils/RevisionWalker.ts +427 -457
  345. package/src/utils/SelectiveRevisionAcceptor.ts +662 -683
  346. package/src/utils/ShadingResolver.ts +105 -107
  347. package/src/utils/acceptRevisions.ts +723 -714
  348. package/src/utils/cnfStyleDecoder.ts +212 -217
  349. package/src/utils/corruptionDetection.ts +346 -345
  350. package/src/utils/dateFormatting.ts +20 -20
  351. package/src/utils/deepClone.ts +77 -78
  352. package/src/utils/diagnostics.ts +125 -129
  353. package/src/utils/errorHandling.ts +80 -80
  354. package/src/utils/formatting.ts +220 -213
  355. package/src/utils/list-detection.ts +32 -42
  356. package/src/utils/logger.ts +412 -404
  357. package/src/utils/parsingHelpers.ts +190 -190
  358. package/src/utils/stripTrackedChanges.ts +356 -353
  359. package/src/utils/textDiff.ts +100 -100
  360. package/src/utils/units.ts +421 -421
  361. package/src/utils/validation.ts +553 -542
  362. package/src/utils/xmlSanitization.ts +179 -182
  363. package/src/validation/RevisionAutoFixer.ts +541 -542
  364. package/src/validation/RevisionValidator.ts +470 -460
  365. package/src/validation/ValidationRules.ts +338 -338
  366. package/src/validation/index.ts +30 -30
  367. package/src/xml/XMLBuilder.ts +857 -871
  368. package/src/xml/XMLParser.ts +877 -919
  369. package/src/zip/ZipHandler.ts +629 -637
  370. package/src/zip/ZipReader.ts +295 -299
  371. package/src/zip/ZipWriter.ts +374 -390
  372. 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
+ }