@caribeng/ui 22.2.0 → 22.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/README.md +311 -231
  2. package/fesm2022/caribeng-ui-accordion.mjs +2 -2
  3. package/fesm2022/caribeng-ui-account-picker.mjs +533 -0
  4. package/fesm2022/caribeng-ui-aging.mjs +488 -0
  5. package/fesm2022/{caribeng-ui-caribe-alert.mjs → caribeng-ui-alert.mjs} +45 -45
  6. package/fesm2022/caribeng-ui-amount.mjs +249 -0
  7. package/fesm2022/caribeng-ui-anchor.mjs +2 -2
  8. package/fesm2022/{caribeng-ui-caribe-animation-lottie.mjs → caribeng-ui-animation-lottie.mjs} +41 -41
  9. package/fesm2022/caribeng-ui-audit-log.mjs +323 -0
  10. package/fesm2022/caribeng-ui-avatar.mjs +4 -4
  11. package/fesm2022/caribeng-ui-badge.mjs +8 -2
  12. package/fesm2022/caribeng-ui-breadcrumb.mjs +26 -9
  13. package/fesm2022/{caribeng-ui-caribe-button-group.mjs → caribeng-ui-button-group.mjs} +15 -15
  14. package/fesm2022/caribeng-ui-button.mjs +2 -2
  15. package/fesm2022/{caribeng-ui-caribe-card-content.mjs → caribeng-ui-card-content.mjs} +39 -39
  16. package/fesm2022/{caribeng-ui-caribe-card-move.mjs → caribeng-ui-card-move.mjs} +65 -65
  17. package/fesm2022/{caribeng-ui-caribe-card-primary.mjs → caribeng-ui-card-primary.mjs} +31 -31
  18. package/fesm2022/{caribeng-ui-caribe-card-select.mjs → caribeng-ui-card-select.mjs} +67 -67
  19. package/fesm2022/caribeng-ui-carousel.mjs +2 -2
  20. package/fesm2022/caribeng-ui-cascadeselect.mjs +45 -9
  21. package/fesm2022/caribeng-ui-chart.mjs +8 -5
  22. package/fesm2022/caribeng-ui-chat.mjs +1303 -0
  23. package/fesm2022/{caribeng-ui-caribe-coachmark.mjs → caribeng-ui-coachmark.mjs} +85 -85
  24. package/fesm2022/caribeng-ui-code-block.mjs +332 -0
  25. package/fesm2022/caribeng-ui-colorpicker.mjs +99 -5
  26. package/fesm2022/caribeng-ui-command-palette.mjs +476 -0
  27. package/fesm2022/caribeng-ui-comments.mjs +864 -0
  28. package/fesm2022/caribeng-ui-confirmdialog.mjs +78 -13
  29. package/fesm2022/caribeng-ui-confirmpopup.mjs +91 -16
  30. package/fesm2022/caribeng-ui-contextmenu.mjs +8 -4
  31. package/fesm2022/caribeng-ui-copy-button.mjs +230 -0
  32. package/fesm2022/caribeng-ui-datagrid.mjs +1635 -0
  33. package/fesm2022/caribeng-ui-dataview.mjs +6 -3
  34. package/fesm2022/caribeng-ui-datepicker.mjs +2 -2
  35. package/fesm2022/caribeng-ui-dock.mjs +52 -12
  36. package/fesm2022/caribeng-ui-document.mjs +495 -0
  37. package/fesm2022/{caribeng-ui-caribe-dots-indicator.mjs → caribeng-ui-dots-indicator.mjs} +41 -41
  38. package/fesm2022/caribeng-ui-drawer.mjs +9 -4
  39. package/fesm2022/caribeng-ui-dynamic-form.mjs +1581 -0
  40. package/fesm2022/caribeng-ui-editor.mjs +2324 -1311
  41. package/fesm2022/{caribeng-ui-caribe-expansion-panel.mjs → caribeng-ui-expansion-panel.mjs} +39 -39
  42. package/fesm2022/{caribeng-ui-caribe-fab-menu.mjs → caribeng-ui-fab-menu.mjs} +75 -75
  43. package/fesm2022/caribeng-ui-fieldset.mjs +2 -2
  44. package/fesm2022/caribeng-ui-file-card.mjs +134 -17
  45. package/fesm2022/{caribeng-ui-caribe-file-download.mjs → caribeng-ui-file-download.mjs} +63 -63
  46. package/fesm2022/caribeng-ui-financial-report.mjs +380 -0
  47. package/fesm2022/{caribeng-ui-caribe-float-menu.mjs → caribeng-ui-float-menu.mjs} +31 -31
  48. package/fesm2022/caribeng-ui-floatlabel.mjs +2 -2
  49. package/fesm2022/caribeng-ui-flow.mjs +1508 -0
  50. package/fesm2022/caribeng-ui-folder-card.mjs +122 -12
  51. package/fesm2022/{caribeng-ui-caribe-footer.mjs → caribeng-ui-footer.mjs} +19 -19
  52. package/fesm2022/caribeng-ui-gantt.mjs +1094 -0
  53. package/fesm2022/{caribeng-ui-caribe-header.mjs → caribeng-ui-header.mjs} +49 -49
  54. package/fesm2022/{caribeng-ui-caribe-icon-button.mjs → caribeng-ui-icon-button.mjs} +23 -23
  55. package/fesm2022/caribeng-ui-image-cropper.mjs +671 -0
  56. package/fesm2022/caribeng-ui-import-wizard.mjs +746 -0
  57. package/fesm2022/caribeng-ui-infinite-scroll.mjs +226 -0
  58. package/fesm2022/caribeng-ui-inlinemessage.mjs +2 -2
  59. package/fesm2022/caribeng-ui-input-number.mjs +22 -6
  60. package/fesm2022/{caribeng-ui-caribe-input-search.mjs → caribeng-ui-input-search.mjs} +65 -65
  61. package/fesm2022/{caribeng-ui-caribe-input-token.mjs → caribeng-ui-input-token.mjs} +59 -59
  62. package/fesm2022/caribeng-ui-inputmask.mjs +8 -2
  63. package/fesm2022/caribeng-ui-inputotp.mjs +2 -2
  64. package/fesm2022/caribeng-ui-journal-entry.mjs +340 -0
  65. package/fesm2022/caribeng-ui-kanban.mjs +4612 -0
  66. package/fesm2022/caribeng-ui-kbd.mjs +122 -0
  67. package/fesm2022/caribeng-ui-kpi.mjs +189 -0
  68. package/fesm2022/{caribeng-ui-caribe-link.mjs → caribeng-ui-link.mjs} +37 -37
  69. package/fesm2022/caribeng-ui-masonry.mjs +93 -0
  70. package/fesm2022/caribeng-ui-media-player.mjs +821 -0
  71. package/fesm2022/caribeng-ui-megamenu.mjs +13 -9
  72. package/fesm2022/caribeng-ui-mentions.mjs +495 -0
  73. package/fesm2022/caribeng-ui-menu.mjs +8 -4
  74. package/fesm2022/caribeng-ui-menubar.mjs +9 -5
  75. package/fesm2022/caribeng-ui-message.mjs +6 -3
  76. package/fesm2022/caribeng-ui-notification-center.mjs +636 -0
  77. package/fesm2022/{caribeng-ui-caribe-off-canvas.mjs → caribeng-ui-off-canvas.mjs} +55 -55
  78. package/fesm2022/{caribeng-ui-caribe-onboarding.mjs → caribeng-ui-onboarding.mjs} +105 -105
  79. package/fesm2022/caribeng-ui-orderlist.mjs +2 -2
  80. package/fesm2022/caribeng-ui-organizationchart.mjs +6 -10
  81. package/fesm2022/caribeng-ui-panel.mjs +6 -6
  82. package/fesm2022/caribeng-ui-panelmenu.mjs +9 -6
  83. package/fesm2022/caribeng-ui-pdfviewer.mjs +1755 -25
  84. package/fesm2022/caribeng-ui-period-picker.mjs +637 -0
  85. package/fesm2022/caribeng-ui-phone-input.mjs +688 -0
  86. package/fesm2022/caribeng-ui-picklist.mjs +2 -2
  87. package/fesm2022/caribeng-ui-pivot.mjs +419 -0
  88. package/fesm2022/caribeng-ui-pricing.mjs +263 -69
  89. package/fesm2022/caribeng-ui-qrcode.mjs +564 -0
  90. package/fesm2022/caribeng-ui-rating.mjs +14 -17
  91. package/fesm2022/caribeng-ui-reconciliation.mjs +386 -0
  92. package/fesm2022/caribeng-ui-ripple.mjs +2 -0
  93. package/fesm2022/caribeng-ui-scheduler.mjs +1027 -0
  94. package/fesm2022/caribeng-ui-scrolltop.mjs +3 -1
  95. package/fesm2022/caribeng-ui-select.mjs +2 -2
  96. package/fesm2022/{caribeng-ui-caribe-shortcut.mjs → caribeng-ui-shortcut.mjs} +29 -29
  97. package/fesm2022/caribeng-ui-sidebar.mjs +573 -0
  98. package/fesm2022/caribeng-ui-signature-pad.mjs +654 -0
  99. package/fesm2022/{caribeng-ui-caribe-status.mjs → caribeng-ui-status.mjs} +11 -11
  100. package/fesm2022/{caribeng-ui-caribe-step-indicator.mjs → caribeng-ui-step-indicator.mjs} +53 -53
  101. package/fesm2022/caribeng-ui-stepper.mjs +12 -5
  102. package/fesm2022/caribeng-ui-steps.mjs +2 -2
  103. package/fesm2022/caribeng-ui-table.mjs +333 -19
  104. package/fesm2022/caribeng-ui-tabmenu.mjs +10 -6
  105. package/fesm2022/caribeng-ui-tabs.mjs +2 -2
  106. package/fesm2022/{caribeng-ui-caribe-tag-button.mjs → caribeng-ui-tag-button.mjs} +25 -25
  107. package/fesm2022/caribeng-ui-tag.mjs +2 -2
  108. package/fesm2022/caribeng-ui-tieredmenu.mjs +111 -21
  109. package/fesm2022/{caribeng-ui-caribe-tracker.mjs → caribeng-ui-tracker.mjs} +29 -29
  110. package/fesm2022/{caribeng-ui-caribe-transaction-status.mjs → caribeng-ui-transaction-status.mjs} +11 -11
  111. package/fesm2022/caribeng-ui-treeselect.mjs +44 -9
  112. package/fesm2022/caribeng-ui-treetable.mjs +236 -17
  113. package/fesm2022/{caribeng-ui-caribe-voucher.mjs → caribeng-ui-voucher.mjs} +45 -45
  114. package/fesm2022/caribeng-ui-xlsx.mjs +486 -0
  115. package/fesm2022/caribeng-ui.mjs +1568 -1
  116. package/package.json +258 -178
  117. package/schematics/collection.json +5 -0
  118. package/schematics/migrate-from-primeng/core-icons.json +1 -0
  119. package/schematics/migrate-from-primeng/icons.js +269 -0
  120. package/schematics/migrate-from-primeng/index.js +290 -0
  121. package/schematics/migrate-from-primeng/mappings/button.js +93 -0
  122. package/schematics/migrate-from-primeng/mappings/card.js +33 -0
  123. package/schematics/migrate-from-primeng/mappings/checkbox.js +44 -0
  124. package/schematics/migrate-from-primeng/mappings/confirm.js +221 -0
  125. package/schematics/migrate-from-primeng/mappings/datepicker.js +121 -0
  126. package/schematics/migrate-from-primeng/mappings/dialog.js +84 -0
  127. package/schematics/migrate-from-primeng/mappings/index.js +88 -0
  128. package/schematics/migrate-from-primeng/mappings/inputnumber.js +62 -0
  129. package/schematics/migrate-from-primeng/mappings/inputtext.js +60 -0
  130. package/schematics/migrate-from-primeng/mappings/menu.js +468 -0
  131. package/schematics/migrate-from-primeng/mappings/multiselect.js +49 -0
  132. package/schematics/migrate-from-primeng/mappings/radiobutton.js +22 -0
  133. package/schematics/migrate-from-primeng/mappings/rules.js +206 -0
  134. package/schematics/migrate-from-primeng/mappings/select.js +97 -0
  135. package/schematics/migrate-from-primeng/mappings/service-calls.js +100 -0
  136. package/schematics/migrate-from-primeng/mappings/table.js +613 -0
  137. package/schematics/migrate-from-primeng/mappings/tabs.js +225 -0
  138. package/schematics/migrate-from-primeng/mappings/tag.js +29 -0
  139. package/schematics/migrate-from-primeng/mappings/textarea.js +44 -0
  140. package/schematics/migrate-from-primeng/mappings/toast.js +173 -0
  141. package/schematics/migrate-from-primeng/mappings/toggleswitch.js +37 -0
  142. package/schematics/migrate-from-primeng/mappings/tooltip.js +44 -0
  143. package/schematics/migrate-from-primeng/mappings/types.js +10 -0
  144. package/schematics/migrate-from-primeng/report.js +79 -0
  145. package/schematics/migrate-from-primeng/schema.js +2 -0
  146. package/schematics/migrate-from-primeng/schema.json +34 -0
  147. package/schematics/migrate-from-primeng/template.js +387 -0
  148. package/schematics/migrate-from-primeng/todo.js +5 -0
  149. package/schematics/migrate-from-primeng/typescript.js +442 -0
  150. package/schematics/migrate-from-primeng/workspace.js +117 -0
  151. package/schematics/ng-add/index.js +16 -1
  152. package/types/caribeng-ui-account-picker.d.ts +142 -0
  153. package/types/caribeng-ui-aging.d.ts +123 -0
  154. package/types/{caribeng-ui-caribe-alert.d.ts → caribeng-ui-alert.d.ts} +2 -2
  155. package/types/caribeng-ui-amount.d.ts +82 -0
  156. package/types/{caribeng-ui-caribe-animation-lottie.d.ts → caribeng-ui-animation-lottie.d.ts} +2 -2
  157. package/types/caribeng-ui-audit-log.d.ts +86 -0
  158. package/types/caribeng-ui-badge.d.ts +3 -1
  159. package/types/caribeng-ui-breadcrumb.d.ts +9 -1
  160. package/types/{caribeng-ui-caribe-button-group.d.ts → caribeng-ui-button-group.d.ts} +2 -2
  161. package/types/{caribeng-ui-caribe-card-content.d.ts → caribeng-ui-card-content.d.ts} +2 -2
  162. package/types/{caribeng-ui-caribe-card-move.d.ts → caribeng-ui-card-move.d.ts} +1 -1
  163. package/types/{caribeng-ui-caribe-card-primary.d.ts → caribeng-ui-card-primary.d.ts} +2 -2
  164. package/types/{caribeng-ui-caribe-card-select.d.ts → caribeng-ui-card-select.d.ts} +2 -2
  165. package/types/caribeng-ui-cascadeselect.d.ts +6 -0
  166. package/types/caribeng-ui-chart.d.ts +3 -1
  167. package/types/caribeng-ui-chat.d.ts +299 -0
  168. package/types/{caribeng-ui-caribe-coachmark.d.ts → caribeng-ui-coachmark.d.ts} +2 -2
  169. package/types/caribeng-ui-code-block.d.ts +65 -0
  170. package/types/caribeng-ui-colorpicker.d.ts +5 -1
  171. package/types/caribeng-ui-command-palette.d.ts +133 -0
  172. package/types/caribeng-ui-comments.d.ts +214 -0
  173. package/types/caribeng-ui-confirmdialog.d.ts +34 -2
  174. package/types/caribeng-ui-confirmpopup.d.ts +39 -2
  175. package/types/caribeng-ui-contextmenu.d.ts +7 -0
  176. package/types/caribeng-ui-copy-button.d.ts +90 -0
  177. package/types/caribeng-ui-datagrid.d.ts +269 -0
  178. package/types/caribeng-ui-dataview.d.ts +2 -0
  179. package/types/caribeng-ui-dock.d.ts +13 -1
  180. package/types/caribeng-ui-document.d.ts +138 -0
  181. package/types/{caribeng-ui-caribe-dots-indicator.d.ts → caribeng-ui-dots-indicator.d.ts} +2 -2
  182. package/types/caribeng-ui-drawer.d.ts +3 -1
  183. package/types/caribeng-ui-dynamic-form.d.ts +409 -0
  184. package/types/caribeng-ui-editor.d.ts +110 -12
  185. package/types/{caribeng-ui-caribe-expansion-panel.d.ts → caribeng-ui-expansion-panel.d.ts} +1 -1
  186. package/types/{caribeng-ui-caribe-fab-menu.d.ts → caribeng-ui-fab-menu.d.ts} +2 -2
  187. package/types/caribeng-ui-file-card.d.ts +27 -2
  188. package/types/{caribeng-ui-caribe-file-download.d.ts → caribeng-ui-file-download.d.ts} +1 -1
  189. package/types/caribeng-ui-financial-report.d.ts +102 -0
  190. package/types/{caribeng-ui-caribe-float-menu.d.ts → caribeng-ui-float-menu.d.ts} +2 -2
  191. package/types/caribeng-ui-flow.d.ts +228 -0
  192. package/types/caribeng-ui-folder-card.d.ts +27 -5
  193. package/types/{caribeng-ui-caribe-footer.d.ts → caribeng-ui-footer.d.ts} +2 -2
  194. package/types/caribeng-ui-gantt.d.ts +244 -0
  195. package/types/{caribeng-ui-caribe-header.d.ts → caribeng-ui-header.d.ts} +2 -2
  196. package/types/{caribeng-ui-caribe-icon-button.d.ts → caribeng-ui-icon-button.d.ts} +2 -2
  197. package/types/{caribeng-ui-caribe-illustration.d.ts → caribeng-ui-illustration.d.ts} +1 -1
  198. package/types/caribeng-ui-image-cropper.d.ts +169 -0
  199. package/types/caribeng-ui-import-wizard.d.ts +149 -0
  200. package/types/caribeng-ui-infinite-scroll.d.ts +75 -0
  201. package/types/caribeng-ui-input-number.d.ts +1 -1
  202. package/types/{caribeng-ui-caribe-input-search.d.ts → caribeng-ui-input-search.d.ts} +2 -2
  203. package/types/{caribeng-ui-caribe-input-token.d.ts → caribeng-ui-input-token.d.ts} +2 -2
  204. package/types/caribeng-ui-inputmask.d.ts +3 -1
  205. package/types/caribeng-ui-journal-entry.d.ts +110 -0
  206. package/types/caribeng-ui-kanban.d.ts +783 -0
  207. package/types/caribeng-ui-kbd.d.ts +36 -0
  208. package/types/caribeng-ui-kpi.d.ts +64 -0
  209. package/types/{caribeng-ui-caribe-link.d.ts → caribeng-ui-link.d.ts} +2 -2
  210. package/types/{caribeng-ui-caribe-logo.d.ts → caribeng-ui-logo.d.ts} +1 -1
  211. package/types/caribeng-ui-masonry.d.ts +32 -0
  212. package/types/caribeng-ui-media-player.d.ts +140 -0
  213. package/types/caribeng-ui-megamenu.d.ts +11 -0
  214. package/types/caribeng-ui-mentions.d.ts +127 -0
  215. package/types/caribeng-ui-menu.d.ts +7 -0
  216. package/types/caribeng-ui-menubar.d.ts +7 -0
  217. package/types/caribeng-ui-message.d.ts +2 -0
  218. package/types/caribeng-ui-notification-center.d.ts +153 -0
  219. package/types/{caribeng-ui-caribe-off-canvas.d.ts → caribeng-ui-off-canvas.d.ts} +2 -2
  220. package/types/{caribeng-ui-caribe-onboarding.d.ts → caribeng-ui-onboarding.d.ts} +2 -2
  221. package/types/caribeng-ui-panelmenu.d.ts +7 -0
  222. package/types/caribeng-ui-pdfviewer.d.ts +245 -5
  223. package/types/caribeng-ui-period-picker.d.ts +148 -0
  224. package/types/caribeng-ui-phone-input.d.ts +168 -0
  225. package/types/caribeng-ui-pivot.d.ts +116 -0
  226. package/types/caribeng-ui-pricing.d.ts +41 -4
  227. package/types/caribeng-ui-qrcode.d.ts +88 -0
  228. package/types/caribeng-ui-reconciliation.d.ts +109 -0
  229. package/types/caribeng-ui-scheduler.d.ts +188 -0
  230. package/types/{caribeng-ui-caribe-shortcut.d.ts → caribeng-ui-shortcut.d.ts} +2 -2
  231. package/types/caribeng-ui-sidebar.d.ts +167 -0
  232. package/types/caribeng-ui-signature-pad.d.ts +151 -0
  233. package/types/{caribeng-ui-caribe-status.d.ts → caribeng-ui-status.d.ts} +2 -2
  234. package/types/{caribeng-ui-caribe-step-indicator.d.ts → caribeng-ui-step-indicator.d.ts} +2 -2
  235. package/types/caribeng-ui-stepper.d.ts +2 -0
  236. package/types/caribeng-ui-table.d.ts +67 -3
  237. package/types/caribeng-ui-tabmenu.d.ts +7 -0
  238. package/types/{caribeng-ui-caribe-tag-button.d.ts → caribeng-ui-tag-button.d.ts} +2 -2
  239. package/types/caribeng-ui-tieredmenu.d.ts +17 -1
  240. package/types/{caribeng-ui-caribe-tracker.d.ts → caribeng-ui-tracker.d.ts} +2 -2
  241. package/types/{caribeng-ui-caribe-transaction-status.d.ts → caribeng-ui-transaction-status.d.ts} +2 -2
  242. package/types/caribeng-ui-treeselect.d.ts +6 -0
  243. package/types/caribeng-ui-treetable.d.ts +67 -2
  244. package/types/{caribeng-ui-caribe-voucher.d.ts → caribeng-ui-voucher.d.ts} +2 -2
  245. package/types/caribeng-ui-xlsx.d.ts +79 -0
  246. package/types/caribeng-ui.d.ts +985 -2
  247. package/fesm2022/caribeng-ui-caribe-calendar.mjs +0 -92
  248. package/fesm2022/caribeng-ui-caribe-circle-loading.mjs +0 -130
  249. package/fesm2022/caribeng-ui-caribe-data-table.mjs +0 -94
  250. package/fesm2022/caribeng-ui-caribe-file-select.mjs +0 -88
  251. package/fesm2022/caribeng-ui-caribe-inline-alert.mjs +0 -100
  252. package/fesm2022/caribeng-ui-caribe-input-date.mjs +0 -138
  253. package/fesm2022/caribeng-ui-caribe-input-select.mjs +0 -154
  254. package/fesm2022/caribeng-ui-caribe-loader.mjs +0 -48
  255. package/fesm2022/caribeng-ui-caribe-modal.mjs +0 -119
  256. package/fesm2022/caribeng-ui-caribe-pictogram.mjs +0 -52
  257. package/fesm2022/caribeng-ui-caribe-pop-over.mjs +0 -104
  258. package/fesm2022/caribeng-ui-caribe-preloader.mjs +0 -57
  259. package/fesm2022/caribeng-ui-caribe-progress-bar.mjs +0 -84
  260. package/fesm2022/caribeng-ui-caribe-stroke.mjs +0 -69
  261. package/fesm2022/caribeng-ui-caribe-switch.mjs +0 -182
  262. package/styles/caribeng.css +0 -380
  263. package/styles/scss/_mixins.scss +0 -46
  264. package/styles/scss/_overlay.scss +0 -15
  265. package/styles/scss/_tokens.scss +0 -267
  266. package/styles/scss/_utilities.scss +0 -73
  267. package/styles/scss/_variables.scss +0 -33
  268. package/styles/scss/index.scss +0 -16
  269. package/types/caribeng-ui-caribe-calendar.d.ts +0 -27
  270. package/types/caribeng-ui-caribe-circle-loading.d.ts +0 -28
  271. package/types/caribeng-ui-caribe-data-table.d.ts +0 -26
  272. package/types/caribeng-ui-caribe-file-select.d.ts +0 -31
  273. package/types/caribeng-ui-caribe-inline-alert.d.ts +0 -28
  274. package/types/caribeng-ui-caribe-input-date.d.ts +0 -40
  275. package/types/caribeng-ui-caribe-input-select.d.ts +0 -47
  276. package/types/caribeng-ui-caribe-loader.d.ts +0 -22
  277. package/types/caribeng-ui-caribe-modal.d.ts +0 -21
  278. package/types/caribeng-ui-caribe-pictogram.d.ts +0 -15
  279. package/types/caribeng-ui-caribe-pop-over.d.ts +0 -17
  280. package/types/caribeng-ui-caribe-preloader.d.ts +0 -24
  281. package/types/caribeng-ui-caribe-progress-bar.d.ts +0 -28
  282. package/types/caribeng-ui-caribe-stroke.d.ts +0 -17
  283. package/types/caribeng-ui-caribe-switch.d.ts +0 -55
  284. /package/fesm2022/{caribeng-ui-caribe-illustration.mjs → caribeng-ui-illustration.mjs} +0 -0
  285. /package/fesm2022/{caribeng-ui-caribe-logo.mjs → caribeng-ui-logo.mjs} +0 -0
@@ -1,11 +1,337 @@
1
- import { DOCUMENT, isPlatformBrowser } from '@angular/common';
1
+ import { DOCUMENT, isPlatformBrowser, NgTemplateOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { InjectionToken, makeEnvironmentProviders, inject, Injectable, input, booleanAttribute, model, output, Injector, PLATFORM_ID, signal, viewChild, viewChildren, computed, effect, untracked, afterNextRender, DestroyRef, isDevMode, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { InjectionToken, makeEnvironmentProviders, inject, Injectable, input, booleanAttribute, model, output, Injector, ElementRef, PLATFORM_ID, signal, viewChild, viewChildren, computed, effect, untracked, afterNextRender, DestroyRef, isDevMode, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
4
4
  import { injectCbTranslation, cbFormat } from '@caribeng/ui';
5
+ import { CbDialog } from '@caribeng/ui/dialog';
5
6
  import { CbIconRegistry, CbIcon } from '@caribeng/ui/icon';
6
- import { panelLeft, printer, zoomOut, zoomIn } from '@caribeng/ui/icons';
7
+ import { signature, penLine, stickyNote, highlighter, panelLeft, printer, zoomOut, zoomIn } from '@caribeng/ui/icons';
8
+ import { CbSignaturePad } from '@caribeng/ui/signature-pad';
7
9
  import { CbSpinner } from '@caribeng/ui/spinner';
8
10
 
11
+ const CB_PDF_COLORS = {
12
+ yellow: { light: '#fde047', dark: '#a16207' },
13
+ green: { light: '#86efac', dark: '#15803d' },
14
+ blue: { light: '#93c5fd', dark: '#1d4ed8' },
15
+ red: { light: '#fca5a5', dark: '#b91c1c' },
16
+ };
17
+ /** Punto PDF → píxeles CSS dentro de la página dibujada. */
18
+ function pdfToView(geom, view, x, y) {
19
+ const f = view.factor;
20
+ const u = (x - geom.x0) * geom.userUnit * f;
21
+ const v = (geom.height - (y - geom.y0) * geom.userUnit) * f;
22
+ const w = geom.width * f;
23
+ const h = geom.height * f;
24
+ switch (normalize(view.rotation)) {
25
+ case 90:
26
+ return [h - v, u];
27
+ case 180:
28
+ return [w - u, h - v];
29
+ case 270:
30
+ return [v, w - u];
31
+ default:
32
+ return [u, v];
33
+ }
34
+ }
35
+ /** Píxeles CSS dentro de la página dibujada → punto PDF. */
36
+ function viewToPdf(geom, view, px, py) {
37
+ const f = view.factor;
38
+ const w = geom.width * f;
39
+ const h = geom.height * f;
40
+ let u;
41
+ let v;
42
+ switch (normalize(view.rotation)) {
43
+ case 90:
44
+ [u, v] = [py, h - px];
45
+ break;
46
+ case 180:
47
+ [u, v] = [w - px, h - py];
48
+ break;
49
+ case 270:
50
+ [u, v] = [w - py, px];
51
+ break;
52
+ default:
53
+ [u, v] = [px, py];
54
+ }
55
+ return [geom.x0 + u / (geom.userUnit * f), geom.y0 + (geom.height - v / f) / geom.userUnit];
56
+ }
57
+ /** Rectángulo PDF → caja CSS (`left`, `top`, `width`, `height`) en la página dibujada. */
58
+ function rectToView(geom, view, rect) {
59
+ const [ax, ay] = pdfToView(geom, view, rect[0], rect[1]);
60
+ const [bx, by] = pdfToView(geom, view, rect[2], rect[3]);
61
+ return { left: Math.min(ax, bx), top: Math.min(ay, by), width: Math.abs(bx - ax), height: Math.abs(by - ay) };
62
+ }
63
+ /** Caja CSS en la página dibujada → rectángulo PDF normalizado (x1 < x2, y1 < y2). */
64
+ function viewToRect(geom, view, left, top, width, height) {
65
+ const [ax, ay] = viewToPdf(geom, view, left, top);
66
+ const [bx, by] = viewToPdf(geom, view, left + width, top + height);
67
+ return [round(Math.min(ax, bx)), round(Math.min(ay, by)), round(Math.max(ax, bx)), round(Math.max(ay, by))];
68
+ }
69
+ /** Desplaza un rectángulo `dx`, `dy` puntos sin salirse de la página. */
70
+ function moveRect(geom, rect, dx, dy) {
71
+ const w = rect[2] - rect[0];
72
+ const h = rect[3] - rect[1];
73
+ const maxX = geom.x0 + geom.width / geom.userUnit - w;
74
+ const maxY = geom.y0 + geom.height / geom.userUnit - h;
75
+ const x = clamp$1(rect[0] + dx, geom.x0, Math.max(geom.x0, maxX));
76
+ const y = clamp$1(rect[1] + dy, geom.y0, Math.max(geom.y0, maxY));
77
+ return [round(x), round(y), round(x + w), round(y + h)];
78
+ }
79
+ /** Escala un rectángulo desde su esquina superior izquierda (en el PDF, x1 e y2 se quedan). */
80
+ function scaleRect(rect, factor, minWidth = 24) {
81
+ const w = rect[2] - rect[0];
82
+ const h = rect[3] - rect[1];
83
+ const k = Math.max(factor, minWidth / Math.max(1, w));
84
+ return [rect[0], round(rect[3] - h * k), round(rect[0] + w * k), rect[3]];
85
+ }
86
+ /**
87
+ * Une los rectángulos de una selección de texto (`getClientRects()`, ya relativos a la página) en
88
+ * uno por línea: los que se solapan en vertical y se tocan en horizontal.
89
+ */
90
+ function mergeLineRects(rects) {
91
+ const sorted = rects.filter((r) => r.width > 0.5 && r.height > 0.5).sort((a, b) => a.top - b.top || a.left - b.left);
92
+ const lines = [];
93
+ for (const r of sorted) {
94
+ const right = r.left + r.width;
95
+ const bottom = r.top + r.height;
96
+ const line = lines.find((l) => {
97
+ const overlap = Math.min(l.bottom, bottom) - Math.max(l.top, r.top);
98
+ return overlap > Math.min(l.bottom - l.top, r.height) / 2 && r.left <= l.right + r.height && right >= l.left - r.height;
99
+ });
100
+ if (line) {
101
+ line.left = Math.min(line.left, r.left);
102
+ line.top = Math.min(line.top, r.top);
103
+ line.right = Math.max(line.right, right);
104
+ line.bottom = Math.max(line.bottom, bottom);
105
+ }
106
+ else {
107
+ lines.push({ left: r.left, top: r.top, right, bottom });
108
+ }
109
+ }
110
+ return lines.map((l) => ({ left: l.left, top: l.top, width: l.right - l.left, height: l.bottom - l.top }));
111
+ }
112
+ /** Rectángulo que contiene a todos. */
113
+ function boundsOf(rects) {
114
+ return [
115
+ Math.min(...rects.map((r) => r[0])),
116
+ Math.min(...rects.map((r) => r[1])),
117
+ Math.max(...rects.map((r) => r[2])),
118
+ Math.max(...rects.map((r) => r[3])),
119
+ ];
120
+ }
121
+ /** Caja de un dibujo, con medio grosor de margen. */
122
+ function inkBounds(ink) {
123
+ const xs = [];
124
+ const ys = [];
125
+ for (const path of ink.paths) {
126
+ for (let i = 0; i + 1 < path.length; i += 2) {
127
+ xs.push(path[i]);
128
+ ys.push(path[i + 1]);
129
+ }
130
+ }
131
+ const m = ink.thickness / 2 + 1;
132
+ return [round(Math.min(...xs) - m), round(Math.min(...ys) - m), round(Math.max(...xs) + m), round(Math.max(...ys) + m)];
133
+ }
134
+ /** Quita puntos casi alineados de un trazo (Ramer–Douglas–Peucker), con tolerancia en las mismas unidades. */
135
+ function simplifyPath(points, tolerance) {
136
+ if (points.length <= 4)
137
+ return [...points];
138
+ const n = points.length / 2;
139
+ const keep = new Uint8Array(n);
140
+ keep[0] = keep[n - 1] = 1;
141
+ const stack = [[0, n - 1]];
142
+ while (stack.length) {
143
+ const [a, b] = stack.pop();
144
+ const [ax, ay, bx, by] = [points[a * 2], points[a * 2 + 1], points[b * 2], points[b * 2 + 1]];
145
+ const len = Math.hypot(bx - ax, by - ay) || 1;
146
+ let max = 0;
147
+ let at = -1;
148
+ for (let i = a + 1; i < b; i++) {
149
+ const d = Math.abs((by - ay) * points[i * 2] - (bx - ax) * points[i * 2 + 1] + bx * ay - by * ax) / len;
150
+ if (d > max)
151
+ [max, at] = [d, i];
152
+ }
153
+ if (at !== -1 && max > tolerance) {
154
+ keep[at] = 1;
155
+ stack.push([a, at], [at, b]);
156
+ }
157
+ }
158
+ const out = [];
159
+ for (let i = 0; i < n; i++)
160
+ if (keep[i])
161
+ out.push(points[i * 2], points[i * 2 + 1]);
162
+ return out;
163
+ }
164
+ /** Prefijo que pdf.js reconoce como anotación nueva al guardar o imprimir. */
165
+ const PDFJS_EDITOR_PREFIX = 'pdfjs_internal_editor_';
166
+ /** Tipos de anotación nueva de pdf.js (`AnnotationEditorType`). */
167
+ const EDITOR_TYPE = { freeText: 3, highlight: 9, stamp: 13, ink: 15 };
168
+ /**
169
+ * La anotación en el formato que pdf.js escribe en el PDF al guardar (`saveDocument`) o imprimir.
170
+ * La firma necesita su imagen como `ImageBitmap` (`bitmap`): pdf.js la transfiere al worker, así
171
+ * que hay que crear una nueva cada vez.
172
+ */
173
+ function toPdfJsAnnotation(annotation, pageRotation, bitmap) {
174
+ const base = {
175
+ pageIndex: annotation.page - 1,
176
+ rotation: normalize(pageRotation),
177
+ user: annotation.author || undefined,
178
+ date: annotation.date ? new Date(annotation.date) : new Date(),
179
+ structTreeParentId: null,
180
+ };
181
+ switch (annotation.type) {
182
+ case 'highlight': {
183
+ if (!annotation.rects.length)
184
+ return null;
185
+ const rect = boundsOf(annotation.rects);
186
+ return {
187
+ ...base,
188
+ annotationType: EDITOR_TYPE.highlight,
189
+ color: hexToRgb(CB_PDF_COLORS[annotation.color].light),
190
+ opacity: 1,
191
+ rect,
192
+ quadPoints: annotation.rects.flatMap(([x1, y1, x2, y2]) => [x1, y2, x2, y2, x1, y1, x2, y1]),
193
+ outlines: annotation.rects.map(([x1, y1, x2, y2]) => [x1, y1, x2, y1, x2, y2, x1, y2]),
194
+ popup: annotation.comment ? { contents: annotation.comment, rect: [rect[2], rect[3] - 60, rect[2] + 160, rect[3]] } : undefined,
195
+ };
196
+ }
197
+ case 'note':
198
+ return {
199
+ ...base,
200
+ annotationType: EDITOR_TYPE.freeText,
201
+ color: hexToRgb(CB_PDF_COLORS[annotation.color].dark),
202
+ fontSize: annotation.fontSize ?? 12,
203
+ value: annotation.text,
204
+ rect: [...annotation.rect],
205
+ };
206
+ case 'ink': {
207
+ const paths = annotation.paths.filter((p) => p.length >= 2);
208
+ if (!paths.length)
209
+ return null;
210
+ return {
211
+ ...base,
212
+ annotationType: EDITOR_TYPE.ink,
213
+ color: hexToRgb(CB_PDF_COLORS[annotation.color].dark),
214
+ thickness: annotation.thickness,
215
+ opacity: 1,
216
+ rect: [...inkBounds({ paths, thickness: annotation.thickness })],
217
+ // pdf.js escribe cada trazo como segmentos: NaN×4 indica "línea recta hasta x, y".
218
+ paths: {
219
+ lines: paths.map((p) => {
220
+ const line = [Number.NaN, Number.NaN, Number.NaN, Number.NaN, p[0], p[1]];
221
+ for (let i = 2; i + 1 < p.length; i += 2)
222
+ line.push(Number.NaN, Number.NaN, Number.NaN, Number.NaN, p[i], p[i + 1]);
223
+ return line;
224
+ }),
225
+ points: paths.map((p) => [...p]),
226
+ },
227
+ };
228
+ }
229
+ case 'signature':
230
+ if (!bitmap)
231
+ return null;
232
+ return {
233
+ ...base,
234
+ annotationType: EDITOR_TYPE.stamp,
235
+ bitmapId: `cb_sig_${annotation.id}`,
236
+ bitmap,
237
+ isSvg: false,
238
+ rect: [...annotation.rect],
239
+ };
240
+ }
241
+ }
242
+ /**
243
+ * Recorta el margen transparente de una firma (el área de `cb-signature-pad` suele estar casi
244
+ * vacía) y da su tamaño en píxeles. Sin canvas, la devuelve tal cual.
245
+ */
246
+ async function trimSignature(dataUrl, doc) {
247
+ const img = await loadImage(dataUrl, doc);
248
+ const fallback = { url: dataUrl, width: img?.naturalWidth || 300, height: img?.naturalHeight || 100 };
249
+ if (!img?.naturalWidth)
250
+ return fallback;
251
+ const canvas = doc.createElement('canvas');
252
+ canvas.width = img.naturalWidth;
253
+ canvas.height = img.naturalHeight;
254
+ let ctx = null;
255
+ try {
256
+ ctx = canvas.getContext('2d', { willReadFrequently: true });
257
+ }
258
+ catch {
259
+ ctx = null;
260
+ }
261
+ if (!ctx)
262
+ return fallback;
263
+ ctx.drawImage(img, 0, 0);
264
+ const { data, width, height } = ctx.getImageData(0, 0, canvas.width, canvas.height);
265
+ let [minX, minY, maxX, maxY] = [width, height, -1, -1];
266
+ for (let y = 0; y < height; y++) {
267
+ for (let x = 0; x < width; x++) {
268
+ if (data[(y * width + x) * 4 + 3] > 16) {
269
+ if (x < minX)
270
+ minX = x;
271
+ if (x > maxX)
272
+ maxX = x;
273
+ if (y < minY)
274
+ minY = y;
275
+ if (y > maxY)
276
+ maxY = y;
277
+ }
278
+ }
279
+ }
280
+ if (maxX < 0)
281
+ return fallback;
282
+ const pad = 4;
283
+ const x = Math.max(0, minX - pad);
284
+ const y = Math.max(0, minY - pad);
285
+ const w = Math.min(width, maxX + pad + 1) - x;
286
+ const h = Math.min(height, maxY + pad + 1) - y;
287
+ const out = doc.createElement('canvas');
288
+ out.width = w;
289
+ out.height = h;
290
+ out.getContext('2d')?.drawImage(canvas, x, y, w, h, 0, 0, w, h);
291
+ return { url: out.toDataURL('image/png'), width: w, height: h };
292
+ }
293
+ /** La imagen de una firma como `ImageBitmap`, para que pdf.js la escriba en el PDF. */
294
+ async function imageToBitmap(dataUrl) {
295
+ if (typeof createImageBitmap !== 'function')
296
+ return undefined;
297
+ try {
298
+ const blob = await (await fetch(dataUrl)).blob();
299
+ return await createImageBitmap(blob);
300
+ }
301
+ catch {
302
+ return undefined;
303
+ }
304
+ }
305
+ function loadImage(src, doc) {
306
+ return new Promise((resolve) => {
307
+ const img = doc.createElement('img');
308
+ const timer = setTimeout(() => resolve(null), 3000);
309
+ img.onload = () => {
310
+ clearTimeout(timer);
311
+ resolve(img);
312
+ };
313
+ img.onerror = () => {
314
+ clearTimeout(timer);
315
+ resolve(null);
316
+ };
317
+ img.src = src;
318
+ });
319
+ }
320
+ /** `#rrggbb` → `[r, g, b]` (0–255), como los colores de pdf.js. */
321
+ function hexToRgb(hex) {
322
+ const m = /^#?([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i.exec(hex);
323
+ return m ? [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)] : [0, 0, 0];
324
+ }
325
+ function normalize(rotation) {
326
+ return ((Math.round(rotation / 90) * 90) % 360 + 360) % 360;
327
+ }
328
+ function clamp$1(value, min, max) {
329
+ return Math.min(max, Math.max(min, value));
330
+ }
331
+ function round(value) {
332
+ return Math.round(value * 100) / 100;
333
+ }
334
+
9
335
  const CB_PDF_VIEWER_OPTIONS = new InjectionToken('CB_PDF_VIEWER_OPTIONS');
10
336
  /**
11
337
  * Configura el visor de PDF (worker, recursos y carga de pdf.js).
@@ -79,8 +405,11 @@ const PRINT_DPI = 150;
79
405
  * Imprime el documento: rasteriza cada página a {@link PRINT_DPI} ppp en un
80
406
  * `<iframe>` oculto y abre el diálogo de impresión del navegador (así se
81
407
  * imprime el PDF y no la página de la aplicación).
408
+ *
409
+ * `printStorage` es una copia de los campos y anotaciones nuevas (`annotationStorage.print`): se
410
+ * toma una vez para todas las páginas, porque al copiarla pdf.js transfiere las imágenes al worker.
82
411
  */
83
- async function printPdfDocument(pdf, document, onProgress, rotation = 0) {
412
+ async function printPdfDocument(pdf, document, onProgress, rotation = 0, printStorage = pdf.annotationStorage.print) {
84
413
  const urls = [];
85
414
  const frame = document.createElement('iframe');
86
415
  frame.setAttribute('aria-hidden', 'true');
@@ -96,7 +425,8 @@ async function printPdfDocument(pdf, document, onProgress, rotation = 0) {
96
425
  const canvas = document.createElement('canvas');
97
426
  canvas.width = Math.floor(viewport.width);
98
427
  canvas.height = Math.floor(viewport.height);
99
- await page.render({ canvas, viewport, intent: 'print' }).promise;
428
+ // AnnotationMode.ENABLE_STORAGE (3): los campos se imprimen con lo escrito y con las anotaciones nuevas.
429
+ await page.render({ canvas, viewport, intent: 'print', annotationMode: 3, printAnnotationStorage: printStorage }).promise;
100
430
  const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/png'));
101
431
  if (blob)
102
432
  urls.push(URL.createObjectURL(blob));
@@ -245,11 +575,18 @@ const PAGE_GAP = 16;
245
575
  /** Píxeles máximos de un canvas (evita agotar la memoria en zoom alto). */
246
576
  const MAX_CANVAS_PIXELS = 2 ** 24;
247
577
  const THUMB_WIDTH = 112;
578
+ let nextAnnotationId = 0;
579
+ /** Esquemas que se abren desde los enlaces del documento. */
580
+ const SAFE_URL = /^(https?:|mailto:|tel:)/i;
248
581
  const PDF_ICONS = {
249
582
  'zoom-in': zoomIn,
250
583
  'zoom-out': zoomOut,
251
584
  printer,
252
585
  'panel-left': panelLeft,
586
+ highlighter,
587
+ 'sticky-note': stickyNote,
588
+ 'pen-line': penLine,
589
+ signature,
253
590
  };
254
591
  /**
255
592
  * Visor de PDF sobre [pdf.js](https://mozilla.github.io/pdf.js/) (`pdfjs-dist`,
@@ -257,7 +594,9 @@ const PDF_ICONS = {
257
594
  *
258
595
  * Renderiza las páginas a medida que se ven, con texto seleccionable y
259
596
  * legible por lectores de pantalla, búsqueda (ignora tildes y mayúsculas),
260
- * miniaturas, zoom, giro, descarga, impresión y documentos con contraseña.
597
+ * miniaturas, índice (marcadores), enlaces internos y externos, formularios
598
+ * rellenables (se descargan e imprimen con lo escrito), zoom, giro, descarga,
599
+ * impresión y documentos con contraseña.
261
600
  *
262
601
  * @example
263
602
  * ```html
@@ -299,16 +638,49 @@ class CbPdfViewer {
299
638
  this.showRotate = input(true, { ...(ngDevMode ? { debugName: "showRotate" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
300
639
  this.downloadable = input(true, { ...(ngDevMode ? { debugName: "downloadable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
301
640
  this.printable = input(true, { ...(ngDevMode ? { debugName: "printable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
641
+ /** Pestaña Índice en el panel lateral (si el documento tiene marcadores). */
642
+ this.showOutline = input(true, { ...(ngDevMode ? { debugName: "showOutline" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
643
+ /** Campos de formulario rellenables (si no, se pintan como imagen). */
644
+ this.forms = input(true, { ...(ngDevMode ? { debugName: "forms" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
645
+ /** Enlaces externos del documento: se abren en otra pestaña. `false` los desactiva. */
646
+ this.externalLinks = input(true, { ...(ngDevMode ? { debugName: "externalLinks" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
647
+ /** Vista del panel lateral. */
648
+ this.sidebarView = model('thumbnails', /* @ts-ignore */
649
+ ...(ngDevMode ? [{ debugName: "sidebarView" }] : /* istanbul ignore next */ []));
302
650
  /** Nombre accesible del visor (por defecto, "Visor de PDF"). */
303
651
  this.ariaLabel = input('', /* @ts-ignore */
304
652
  ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
653
+ /** Herramientas para resaltar, poner notas, dibujar y firmar (y la pestaña Anotaciones). */
654
+ this.annotatable = input(false, { ...(ngDevMode ? { debugName: "annotatable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
655
+ /**
656
+ * Anotaciones del usuario, en puntos PDF. Se ven siempre; con `annotatable` se crean, mueven y
657
+ * borran. Al descargar o imprimir se escriben en el PDF como anotaciones estándar.
658
+ */
659
+ this.annotations = model([], /* @ts-ignore */
660
+ ...(ngDevMode ? [{ debugName: "annotations" }] : /* istanbul ignore next */ []));
661
+ /** Herramienta activa (`none`, `highlight`, `note`, `ink`). Escape vuelve a `none`. */
662
+ this.tool = model('none', /* @ts-ignore */
663
+ ...(ngDevMode ? [{ debugName: "tool" }] : /* istanbul ignore next */ []));
664
+ /** Color de las anotaciones nuevas. */
665
+ this.annotationColor = model('yellow', /* @ts-ignore */
666
+ ...(ngDevMode ? [{ debugName: "annotationColor" }] : /* istanbul ignore next */ []));
667
+ /** Autor de las anotaciones nuevas (se guarda en el PDF). */
668
+ this.author = input('', /* @ts-ignore */
669
+ ...(ngDevMode ? [{ debugName: "author" }] : /* istanbul ignore next */ []));
305
670
  /** Documento abierto. */
306
671
  this.loaded = output();
307
672
  /** No se pudo abrir el documento. */
308
673
  this.loadError = output();
674
+ /** El usuario cambió un campo del formulario (los valores, con `getFormData()`). */
675
+ this.formChange = output();
676
+ /** El usuario creó una anotación. */
677
+ this.annotationAdd = output();
678
+ /** El usuario borró una anotación. */
679
+ this.annotationRemove = output();
309
680
  this.engine = inject(CbPdfEngine);
310
681
  this.document = inject(DOCUMENT);
311
682
  this.injector = inject(Injector);
683
+ this.host = inject(ElementRef);
312
684
  this.isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
313
685
  this.uid = `cb-pdf-${++nextUid}`;
314
686
  this.state = signal('idle', /* @ts-ignore */
@@ -334,6 +706,35 @@ class CbPdfViewer {
334
706
  this.searching = signal(false, /* @ts-ignore */
335
707
  ...(ngDevMode ? [{ debugName: "searching" }] : /* istanbul ignore next */ []));
336
708
  this.zoomLevels = ZOOM_LEVELS;
709
+ /** Índice del documento (vacío si no tiene). */
710
+ this.outline = signal([], /* @ts-ignore */
711
+ ...(ngDevMode ? [{ debugName: "outline" }] : /* istanbul ignore next */ []));
712
+ /** Algún campo del formulario cambió desde que se abrió. */
713
+ this.formModified = signal(false, /* @ts-ignore */
714
+ ...(ngDevMode ? [{ debugName: "formModified" }] : /* istanbul ignore next */ []));
715
+ this.collapsed = signal(new Set(), /* @ts-ignore */
716
+ ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
717
+ /** Enlaces del documento (lo que la capa de anotaciones de pdf.js necesita). */
718
+ this.linkService = {
719
+ externalLinkEnabled: true,
720
+ eventBus: null,
721
+ addLinkAttributes: (link, url, newWindow) => {
722
+ if (!this.externalLinks() || !SAFE_URL.test(url)) {
723
+ link.removeAttribute('href');
724
+ return;
725
+ }
726
+ link.href = url;
727
+ link.title = url;
728
+ link.rel = 'noopener noreferrer nofollow';
729
+ if (newWindow !== false)
730
+ link.target = '_blank';
731
+ },
732
+ getDestinationHash: () => '#',
733
+ getAnchorUrl: (hash) => `#${hash}`,
734
+ goToDestination: (dest) => void this.goToDestination(dest),
735
+ executeNamedAction: (action) => this.namedAction(action),
736
+ executeSetOCGState: () => undefined,
737
+ };
337
738
  this.viewport = viewChild('viewport', /* @ts-ignore */
338
739
  ...(ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []));
339
740
  this.pageEls = viewChildren('pageEl', /* @ts-ignore */
@@ -344,6 +745,8 @@ class CbPdfViewer {
344
745
  ...(ngDevMode ? [{ debugName: "searchInput" }] : /* istanbul ignore next */ []));
345
746
  this.passwordInput = viewChild('passwordInput', /* @ts-ignore */
346
747
  ...(ngDevMode ? [{ debugName: "passwordInput" }] : /* istanbul ignore next */ []));
748
+ this.cardText = viewChild('cardText', /* @ts-ignore */
749
+ ...(ngDevMode ? [{ debugName: "cardText" }] : /* istanbul ignore next */ []));
347
750
  /** Factor de zoom efectivo (1 = 100 %). */
348
751
  this.scale = computed(() => {
349
752
  const zoom = this.zoom();
@@ -393,6 +796,79 @@ class CbPdfViewer {
393
796
  return this.searching() ? this.t().searching : this.t().noResults;
394
797
  }, /* @ts-ignore */
395
798
  ...(ngDevMode ? [{ debugName: "searchStatus" }] : /* istanbul ignore next */ []));
799
+ // --- Anotaciones -------------------------------------------------------------
800
+ this.colors = CB_PDF_COLORS;
801
+ this.colorNames = Object.keys(CB_PDF_COLORS);
802
+ this.selectedId = signal(null, /* @ts-ignore */
803
+ ...(ngDevMode ? [{ debugName: "selectedId" }] : /* istanbul ignore next */ []));
804
+ /** Anotación con el editor (texto de la nota o comentario del resaltado) abierto. */
805
+ this.editingId = signal(null, /* @ts-ignore */
806
+ ...(ngDevMode ? [{ debugName: "editingId" }] : /* istanbul ignore next */ []));
807
+ /** Anotación mientras se arrastra o cambia de tamaño (se confirma al soltar). */
808
+ this.dragPreview = signal(null, /* @ts-ignore */
809
+ ...(ngDevMode ? [{ debugName: "dragPreview" }] : /* istanbul ignore next */ []));
810
+ /** Trazo que se está dibujando, en píxeles de la página. */
811
+ this.stroke = signal(null, /* @ts-ignore */
812
+ ...(ngDevMode ? [{ debugName: "stroke" }] : /* istanbul ignore next */ []));
813
+ this.announcement = signal('', /* @ts-ignore */
814
+ ...(ngDevMode ? [{ debugName: "announcement" }] : /* istanbul ignore next */ []));
815
+ this.announceFlip = false;
816
+ this.signing = signal(false, /* @ts-ignore */
817
+ ...(ngDevMode ? [{ debugName: "signing" }] : /* istanbul ignore next */ []));
818
+ this.signatureValue = signal('', /* @ts-ignore */
819
+ ...(ngDevMode ? [{ debugName: "signatureValue" }] : /* istanbul ignore next */ []));
820
+ this.pendingFocus = null;
821
+ this.shownAnnotations = computed(() => {
822
+ const preview = this.dragPreview();
823
+ const list = this.annotations();
824
+ return preview ? list.map((a) => (a.id === preview.id ? preview : a)) : list;
825
+ }, /* @ts-ignore */
826
+ ...(ngDevMode ? [{ debugName: "shownAnnotations" }] : /* istanbul ignore next */ []));
827
+ /** Anotaciones de cada página, ya colocadas con el zoom y el giro actuales. */
828
+ this.marks = computed(() => {
829
+ const sizes = this.sizes();
830
+ const pages = sizes.map(() => []);
831
+ for (const a of this.shownAnnotations()) {
832
+ const size = sizes[a.page - 1];
833
+ if (!size)
834
+ continue;
835
+ pages[a.page - 1].push(this.toMark(a, size));
836
+ }
837
+ return pages;
838
+ }, /* @ts-ignore */
839
+ ...(ngDevMode ? [{ debugName: "marks" }] : /* istanbul ignore next */ []));
840
+ this.editingMark = computed(() => {
841
+ const id = this.editingId();
842
+ if (!id)
843
+ return null;
844
+ for (const page of this.marks())
845
+ for (const m of page)
846
+ if (m.a.id === id)
847
+ return m;
848
+ return null;
849
+ }, /* @ts-ignore */
850
+ ...(ngDevMode ? [{ debugName: "editingMark" }] : /* istanbul ignore next */ []));
851
+ this.sortedAnnotations = computed(() => [...this.annotations()].sort((a, b) => a.page - b.page || this.topOf(b) - this.topOf(a)), /* @ts-ignore */
852
+ ...(ngDevMode ? [{ debugName: "sortedAnnotations" }] : /* istanbul ignore next */ []));
853
+ this.sidebarTabs = computed(() => {
854
+ const tabs = ['thumbnails'];
855
+ if (this.outline().length && this.showOutline())
856
+ tabs.push('outline');
857
+ if (this.annotatable())
858
+ tabs.push('annotations');
859
+ return tabs;
860
+ }, /* @ts-ignore */
861
+ ...(ngDevMode ? [{ debugName: "sidebarTabs" }] : /* istanbul ignore next */ []));
862
+ this.activeView = computed(() => (this.sidebarTabs().includes(this.sidebarView()) ? this.sidebarView() : 'thumbnails'), /* @ts-ignore */
863
+ ...(ngDevMode ? [{ debugName: "activeView" }] : /* istanbul ignore next */ []));
864
+ this.toolHint = computed(() => {
865
+ if (!this.annotatable())
866
+ return '';
867
+ const tool = this.tool();
868
+ const t = this.t();
869
+ return tool === 'highlight' ? t.pdfHighlightHint : tool === 'note' ? t.pdfNoteHint : tool === 'ink' ? t.pdfDrawHint : '';
870
+ }, /* @ts-ignore */
871
+ ...(ngDevMode ? [{ debugName: "toolHint" }] : /* istanbul ignore next */ []));
396
872
  this.loadToken = 0;
397
873
  this.searchToken = 0;
398
874
  this.pages = new Map();
@@ -533,12 +1009,79 @@ class CbPdfViewer {
533
1009
  if (total)
534
1010
  this.selectMatch((this.matchIndex() - 1 + total) % total);
535
1011
  }
536
- /** Descarga el documento original. */
1012
+ /** Lleva a un destino del documento (nombre o destino explícito), con su posición en la página. */
1013
+ async goToDestination(dest) {
1014
+ const pdf = this.pdf;
1015
+ if (!pdf || !dest)
1016
+ return;
1017
+ const explicit = typeof dest === 'string' ? await pdf.getDestination(dest).catch(() => null) : dest;
1018
+ if (!Array.isArray(explicit) || pdf !== this.pdf)
1019
+ return;
1020
+ const ref = explicit[0];
1021
+ let page;
1022
+ try {
1023
+ page = typeof ref === 'object' && ref ? (await pdf.getPageIndex(ref)) + 1 : Number.isInteger(ref) ? ref + 1 : NaN;
1024
+ }
1025
+ catch {
1026
+ return;
1027
+ }
1028
+ if (!Number.isFinite(page) || page < 1 || page > this.numPages())
1029
+ return;
1030
+ // XYZ y FitH/FitBH dan la altura (en puntos, desde abajo) a la que se ve el destino.
1031
+ const kind = explicit[1]?.name;
1032
+ const top = kind === 'XYZ' ? explicit[3] : kind === 'FitH' || kind === 'FitBH' ? explicit[2] : null;
1033
+ this.goToPage(page);
1034
+ if (typeof top === 'number')
1035
+ afterNextRender(() => this.scrollToPoint(page, top), { injector: this.injector });
1036
+ }
1037
+ /** Valores actuales del formulario, por nombre de campo. */
1038
+ async getFormData() {
1039
+ const pdf = this.pdf;
1040
+ if (!pdf)
1041
+ return {};
1042
+ const fields = (await Promise.resolve(pdf.getFieldObjects?.()).catch(() => null));
1043
+ const storage = pdf.annotationStorage;
1044
+ const data = {};
1045
+ if (!fields || !storage)
1046
+ return data;
1047
+ for (const [name, widgets] of fields) {
1048
+ for (const widget of widgets) {
1049
+ if (!widget.id)
1050
+ continue;
1051
+ const stored = storage.getValue(widget.id, { value: widget.value }).value;
1052
+ if (widget.type === 'checkbox' || widget.type === 'radiobutton') {
1053
+ const checked = stored === true || (typeof stored === 'string' && stored !== 'Off' && stored !== '');
1054
+ if (widget.type === 'checkbox')
1055
+ data[name] = checked;
1056
+ else if (checked)
1057
+ data[name] = widget.exportValues ?? String(stored);
1058
+ else
1059
+ data[name] ??= null;
1060
+ }
1061
+ else {
1062
+ data[name] = Array.isArray(stored) ? stored.map(String) : stored == null ? null : String(stored);
1063
+ }
1064
+ }
1065
+ }
1066
+ return data;
1067
+ }
1068
+ /** El PDF con lo escrito en el formulario y las anotaciones (o el original si no hay cambios). */
1069
+ async save() {
1070
+ const pdf = this.pdf;
1071
+ if (!pdf)
1072
+ return null;
1073
+ const changed = this.formModified() || this.annotations().length > 0;
1074
+ const bytes = changed ? await this.withAnnotations(() => pdf.saveDocument()) : await pdf.getData();
1075
+ return new Blob([bytes], { type: 'application/pdf' });
1076
+ }
1077
+ /** Descarga el documento (con los datos del formulario y las anotaciones, si los hay). */
537
1078
  async download() {
538
1079
  if (!this.pdf)
539
1080
  return;
540
1081
  const src = this.src();
541
- const blob = src instanceof Blob ? src : new Blob([(await this.pdf.getData())], { type: 'application/pdf' });
1082
+ const blob = src instanceof Blob && !this.formModified() && !this.annotations().length ? src : await this.save();
1083
+ if (!blob)
1084
+ return;
542
1085
  const url = URL.createObjectURL(blob);
543
1086
  const a = this.document.createElement('a');
544
1087
  a.href = url;
@@ -555,7 +1098,10 @@ class CbPdfViewer {
555
1098
  return;
556
1099
  this.printProgress.set({ done: 0, total: this.numPages() });
557
1100
  try {
558
- await printPdfDocument(this.pdf, this.document, (done, total) => this.printProgress.set({ done, total }), this.rotation());
1101
+ const pdf = this.pdf;
1102
+ // La copia para imprimir se toma con las anotaciones escritas; después se quitan.
1103
+ const storage = await this.withAnnotations(async () => pdf.annotationStorage.print);
1104
+ await printPdfDocument(pdf, this.document, (done, total) => this.printProgress.set({ done, total }), this.rotation(), storage);
559
1105
  }
560
1106
  finally {
561
1107
  this.printProgress.set(null);
@@ -604,6 +1150,14 @@ class CbPdfViewer {
604
1150
  return;
605
1151
  }
606
1152
  this.pdf = pdf;
1153
+ this.formModified.set(false);
1154
+ // pdf.js tipa estos ganchos como `null`, pero los llama si se les asigna una función.
1155
+ const storage = pdf.annotationStorage;
1156
+ if (storage) {
1157
+ storage.onSetModified = () => this.formModified.set(true);
1158
+ storage.onResetModified = () => this.formModified.set(false);
1159
+ }
1160
+ void this.loadOutline(pdf, token);
607
1161
  // Mide el área antes del primer render para que "ajustar al ancho" no renderice dos veces.
608
1162
  this.observeResize();
609
1163
  const first = sizeOf(await this.getPage(1));
@@ -632,6 +1186,14 @@ class CbPdfViewer {
632
1186
  this.loadError.emit(error);
633
1187
  }
634
1188
  }
1189
+ async loadOutline(pdf, token) {
1190
+ const raw = await Promise.resolve(pdf.getOutline?.()).catch(() => null);
1191
+ if (token !== this.loadToken)
1192
+ return;
1193
+ const map = (items) => (items ?? []).map((i) => ({ title: i.title, dest: i.dest, url: i.url, bold: !!i.bold, italic: !!i.italic, items: map(i.items) }));
1194
+ this.outline.set(map(raw));
1195
+ this.collapsed.set(new Set());
1196
+ }
635
1197
  submitPassword(event, value) {
636
1198
  event.preventDefault();
637
1199
  if (!this.passwordCallback)
@@ -681,6 +1243,12 @@ class CbPdfViewer {
681
1243
  this.searching.set(false);
682
1244
  this.numPages.set(0);
683
1245
  this.sizes.set([]);
1246
+ this.outline.set([]);
1247
+ this.formModified.set(false);
1248
+ this.selectedId.set(null);
1249
+ this.editingId.set(null);
1250
+ this.stroke.set(null);
1251
+ this.dragPreview.set(null);
684
1252
  }
685
1253
  getPage(n) {
686
1254
  let page = this.pages.get(n);
@@ -770,12 +1338,15 @@ class CbPdfViewer {
770
1338
  canvas.width = Math.floor(viewport.width * ratio);
771
1339
  canvas.height = Math.floor(viewport.height * ratio);
772
1340
  canvas.setAttribute('aria-hidden', 'true');
773
- const task = page.render({ canvas, viewport, transform: ratio === 1 ? undefined : [ratio, 0, 0, ratio, 0, 0] });
1341
+ // Con formularios HTML, el canvas no dibuja los campos (AnnotationMode.ENABLE_FORMS).
1342
+ const annotationMode = this.forms() ? (this.pdfjs?.AnnotationMode?.ENABLE_FORMS ?? 2) : (this.pdfjs?.AnnotationMode?.ENABLE ?? 1);
1343
+ const task = page.render({ canvas, viewport, annotationMode, transform: ratio === 1 ? undefined : [ratio, 0, 0, ratio, 0, 0] });
774
1344
  renderTask = task;
775
1345
  await task.promise;
776
1346
  if (cancelled)
777
1347
  return;
778
1348
  el.querySelector('.cb-pdf__canvas')?.replaceChildren(canvas);
1349
+ void this.renderAnnotations(page, viewport, el, () => cancelled);
779
1350
  const textEl = el.querySelector('.cb-pdf__text');
780
1351
  if (!textEl || !this.pdfjs?.TextLayer)
781
1352
  return;
@@ -805,6 +1376,54 @@ class CbPdfViewer {
805
1376
  const el = this.pageEl(n);
806
1377
  el?.querySelector('.cb-pdf__canvas')?.replaceChildren();
807
1378
  el?.querySelector('.cb-pdf__text')?.replaceChildren();
1379
+ el?.querySelector('.cb-pdf__annotations')?.replaceChildren();
1380
+ }
1381
+ /** Enlaces y campos de formulario de la página (capa de anotaciones de pdf.js). */
1382
+ async renderAnnotations(page, viewport, el, cancelled) {
1383
+ const div = el.querySelector('.cb-pdf__annotations');
1384
+ const Layer = this.pdfjs?.AnnotationLayer;
1385
+ const pdf = this.pdf;
1386
+ if (!div || !Layer || !pdf)
1387
+ return;
1388
+ try {
1389
+ const annotations = await page.getAnnotations({ intent: 'display' });
1390
+ if (cancelled() || !annotations.length)
1391
+ return;
1392
+ const fieldObjects = this.forms() ? await pdf.getFieldObjects().catch(() => null) : null;
1393
+ if (cancelled())
1394
+ return;
1395
+ div.replaceChildren();
1396
+ const layerViewport = viewport.clone({ dontFlip: true });
1397
+ const layer = new Layer({
1398
+ div,
1399
+ page,
1400
+ viewport: layerViewport,
1401
+ linkService: this.linkService,
1402
+ annotationStorage: pdf.annotationStorage,
1403
+ accessibilityManager: null,
1404
+ annotationCanvasMap: null,
1405
+ annotationEditorUIManager: null,
1406
+ structTreeLayer: null,
1407
+ commentManager: null,
1408
+ });
1409
+ await layer.render({
1410
+ viewport: layerViewport,
1411
+ div,
1412
+ annotations,
1413
+ page,
1414
+ linkService: this.linkService,
1415
+ annotationStorage: pdf.annotationStorage,
1416
+ renderForms: this.forms(),
1417
+ enableScripting: false,
1418
+ hasJSActions: false,
1419
+ fieldObjects,
1420
+ });
1421
+ if (cancelled())
1422
+ div.replaceChildren();
1423
+ }
1424
+ catch {
1425
+ // Anotaciones dañadas: la página se ve igual, sin enlaces ni campos.
1426
+ }
808
1427
  }
809
1428
  observeThumbs() {
810
1429
  const root = this.document.getElementById(`${this.uid}-thumbs`);
@@ -949,6 +1568,16 @@ class CbPdfViewer {
949
1568
  this.visiblePage = n;
950
1569
  root.scrollTop = el.offsetTop - PAGE_GAP;
951
1570
  }
1571
+ /** Desplaza hasta la altura `top` (puntos PDF, desde abajo) de la página `n`. */
1572
+ scrollToPoint(n, top) {
1573
+ const root = this.viewport()?.nativeElement;
1574
+ const el = this.pageEl(n);
1575
+ const size = this.sizes()[n - 1];
1576
+ if (!root || !el || !size || (size.rotate + this.rotation()) % 360 !== 0)
1577
+ return;
1578
+ const factor = this.scale() * CSS_UNITS;
1579
+ root.scrollTop = el.offsetTop + Math.max(0, (size.height - top) * factor) - PAGE_GAP;
1580
+ }
952
1581
  readAnchor() {
953
1582
  const root = this.viewport()?.nativeElement;
954
1583
  const el = this.pageEl(this.visiblePage);
@@ -976,7 +1605,571 @@ class CbPdfViewer {
976
1605
  pageEl(n) {
977
1606
  return this.pageEls()[n - 1]?.nativeElement;
978
1607
  }
1608
+ // --- Anotaciones -------------------------------------------------------------
1609
+ toggleTool(tool) {
1610
+ this.tool.set(this.tool() === tool ? 'none' : tool);
1611
+ }
1612
+ /** Con texto seleccionado lo resalta al momento; si no, activa la herramienta. */
1613
+ onHighlightTool() {
1614
+ if (this.tool() !== 'highlight' && this.highlightSelection())
1615
+ return;
1616
+ this.toggleTool('highlight');
1617
+ }
1618
+ /** Con el teclado (sin puntero) la nota se pone directamente en la página que se ve. */
1619
+ onNoteTool(event) {
1620
+ if (event.detail === 0 && this.tool() !== 'note') {
1621
+ const page = this.page();
1622
+ const spot = this.visibleSpot(page);
1623
+ if (spot)
1624
+ this.addNote(page, spot.x - 80, spot.y - 20);
1625
+ return;
1626
+ }
1627
+ this.toggleTool('note');
1628
+ }
1629
+ openSignature() {
1630
+ this.tool.set('none');
1631
+ this.signatureValue.set('');
1632
+ this.signing.set(true);
1633
+ }
1634
+ async insertSignature() {
1635
+ const value = this.signatureValue();
1636
+ if (!value)
1637
+ return;
1638
+ const page = this.page();
1639
+ const size = this.sizes()[page - 1];
1640
+ const spot = this.visibleSpot(page);
1641
+ this.signing.set(false);
1642
+ if (!size || !spot)
1643
+ return;
1644
+ const { url, width, height } = await trimSignature(value, this.document);
1645
+ const view = this.viewOf(size);
1646
+ const pageWidth = this.pageBoxes()[page - 1]?.width ?? 400;
1647
+ // 180 pt de ancho como máximo (algo menos que media página A4), con la proporción de la firma.
1648
+ const w = Math.min(pageWidth * 0.45, 180 * view.factor);
1649
+ const h = (w * height) / Math.max(1, width);
1650
+ const rect = viewToRect(this.geomOf(size), view, spot.x - w / 2, spot.y - h / 2, w, h);
1651
+ const id = this.addAnnotation({ ...this.newBase(page), type: 'signature', rect, image: url });
1652
+ this.pendingFocus = id;
1653
+ }
1654
+ /** El foco vuelve tras cerrar el diálogo: entonces se lleva a la firma recién puesta. */
1655
+ onSignatureHide() {
1656
+ const id = this.pendingFocus;
1657
+ this.pendingFocus = null;
1658
+ if (id)
1659
+ setTimeout(() => this.focusMark(id));
1660
+ }
1661
+ /** Resalta el texto seleccionado en el documento (una anotación por página). */
1662
+ highlightSelection() {
1663
+ const selection = this.document.getSelection();
1664
+ if (!selection || selection.isCollapsed || !selection.rangeCount)
1665
+ return false;
1666
+ let created = 0;
1667
+ for (const [index, ref] of this.pageEls().entries()) {
1668
+ const pageEl = ref.nativeElement;
1669
+ const textEl = pageEl.querySelector('.cb-pdf__text');
1670
+ const size = this.sizes()[index];
1671
+ if (!textEl || !size)
1672
+ continue;
1673
+ const boxes = [];
1674
+ let text = '';
1675
+ for (let k = 0; k < selection.rangeCount; k++) {
1676
+ const range = selection.getRangeAt(k);
1677
+ if (!range.intersectsNode(textEl))
1678
+ continue;
1679
+ const part = range.cloneRange();
1680
+ if (!textEl.contains(part.startContainer))
1681
+ part.setStart(textEl, 0);
1682
+ if (!textEl.contains(part.endContainer))
1683
+ part.setEnd(textEl, textEl.childNodes.length);
1684
+ const origin = pageEl.getBoundingClientRect();
1685
+ for (const r of Array.from(part.getClientRects())) {
1686
+ boxes.push({ left: r.left - origin.left, top: r.top - origin.top, width: r.width, height: r.height });
1687
+ }
1688
+ text += part.toString();
1689
+ }
1690
+ const lines = mergeLineRects(withoutTallRects(boxes));
1691
+ if (!lines.length)
1692
+ continue;
1693
+ const geom = this.geomOf(size);
1694
+ const view = this.viewOf(size);
1695
+ const rects = lines.map((l) => viewToRect(geom, view, l.left, l.top, l.width, l.height));
1696
+ this.addAnnotation({ ...this.newBase(index + 1), type: 'highlight', rects, color: this.annotationColor(), text: text.replace(/\s+/g, ' ').trim() });
1697
+ created++;
1698
+ }
1699
+ if (created)
1700
+ selection.removeAllRanges();
1701
+ return created > 0;
1702
+ }
1703
+ onPagesPointerUp() {
1704
+ // La selección termina de fijarse después del pointerup.
1705
+ if (this.annotatable() && this.tool() === 'highlight')
1706
+ setTimeout(() => this.highlightSelection());
1707
+ }
1708
+ onMarksPointerDown(event, page) {
1709
+ if (!this.annotatable() || event.button !== 0)
1710
+ return;
1711
+ const layer = event.currentTarget;
1712
+ const origin = layer.getBoundingClientRect();
1713
+ const tool = this.tool();
1714
+ if (tool === 'note') {
1715
+ event.preventDefault();
1716
+ this.addNote(page, event.clientX - origin.left, event.clientY - origin.top);
1717
+ }
1718
+ else if (tool === 'ink') {
1719
+ event.preventDefault();
1720
+ layer.setPointerCapture?.(event.pointerId);
1721
+ this.stroke.set({ page, points: [event.clientX - origin.left, event.clientY - origin.top] });
1722
+ const move = (e) => {
1723
+ const s = this.stroke();
1724
+ if (s)
1725
+ this.stroke.set({ page, points: [...s.points, e.clientX - origin.left, e.clientY - origin.top] });
1726
+ };
1727
+ const end = () => {
1728
+ layer.removeEventListener('pointermove', move);
1729
+ layer.removeEventListener('pointerup', end);
1730
+ layer.removeEventListener('pointercancel', end);
1731
+ this.finishStroke();
1732
+ };
1733
+ layer.addEventListener('pointermove', move);
1734
+ layer.addEventListener('pointerup', end);
1735
+ layer.addEventListener('pointercancel', end);
1736
+ }
1737
+ else if (tool === 'none') {
1738
+ this.closeEditor(false);
1739
+ this.selectedId.set(null);
1740
+ }
1741
+ }
1742
+ finishStroke() {
1743
+ const s = this.stroke();
1744
+ this.stroke.set(null);
1745
+ const size = s && this.sizes()[s.page - 1];
1746
+ if (!s || !size)
1747
+ return;
1748
+ const points = s.points.length === 2 ? [...s.points, s.points[0] + 0.5, s.points[1] + 0.5] : s.points;
1749
+ const geom = this.geomOf(size);
1750
+ const view = this.viewOf(size);
1751
+ const pdf = [];
1752
+ for (let i = 0; i + 1 < points.length; i += 2)
1753
+ pdf.push(...viewToPdf(geom, view, points[i], points[i + 1]));
1754
+ const path = simplifyPath(pdf, 0.4).map((v) => Math.round(v * 100) / 100);
1755
+ this.addAnnotation({ ...this.newBase(s.page), type: 'ink', paths: [path], color: this.annotationColor(), thickness: 2 }, false);
1756
+ }
1757
+ addNote(page, x, y) {
1758
+ const size = this.sizes()[page - 1];
1759
+ if (!size)
1760
+ return;
1761
+ const view = this.viewOf(size);
1762
+ const box = this.pageBoxes()[page - 1];
1763
+ const w = 160 * view.factor;
1764
+ const h = 12 * 1.5 * view.factor;
1765
+ const left = clamp(x, 0, Math.max(0, (box?.width ?? w) - w));
1766
+ const top = clamp(y, 0, Math.max(0, (box?.height ?? h) - h));
1767
+ const rect = viewToRect(this.geomOf(size), view, left, top, w, h);
1768
+ const id = this.addAnnotation({ ...this.newBase(page), type: 'note', rect, text: '', color: this.annotationColor() }, false);
1769
+ this.tool.set('none');
1770
+ this.openEditor(id);
1771
+ }
1772
+ /** Añade una anotación, la anuncia y la selecciona. Devuelve su `id`. */
1773
+ addAnnotation(annotation, focus = true) {
1774
+ this.annotations.update((list) => [...list, annotation]);
1775
+ this.annotationAdd.emit(annotation);
1776
+ this.announce(cbFormat(this.t().pdfAnnotationAdded, this.nounOf(annotation), annotation.page));
1777
+ this.selectedId.set(annotation.id);
1778
+ if (focus)
1779
+ afterNextRender(() => this.focusMark(annotation.id), { injector: this.injector });
1780
+ return annotation.id;
1781
+ }
1782
+ /** Borra una anotación y deja el foco en la siguiente de la página (o en el documento). */
1783
+ removeAnnotation(annotation) {
1784
+ const list = this.annotations();
1785
+ const index = list.findIndex((a) => a.id === annotation.id);
1786
+ if (index === -1)
1787
+ return;
1788
+ const samePage = list.filter((a) => a.page === annotation.page && a.id !== annotation.id);
1789
+ const hadFocus = this.host.nativeElement.contains(this.document.activeElement);
1790
+ this.annotations.set(list.filter((a) => a.id !== annotation.id));
1791
+ if (this.editingId() === annotation.id)
1792
+ this.editingId.set(null);
1793
+ if (this.selectedId() === annotation.id)
1794
+ this.selectedId.set(null);
1795
+ this.annotationRemove.emit(annotation);
1796
+ this.announce(this.t().pdfAnnotationRemoved);
1797
+ if (!hadFocus || this.activeView() === 'annotations' && this.sidebar())
1798
+ return;
1799
+ const next = samePage[0]?.id;
1800
+ afterNextRender(() => (next ? this.focusMark(next) : this.viewport()?.nativeElement.focus()), { injector: this.injector });
1801
+ }
1802
+ replaceAnnotation(annotation) {
1803
+ this.annotations.update((list) => list.map((a) => (a.id === annotation.id ? annotation : a)));
1804
+ }
1805
+ select(id) {
1806
+ if (this.editingId() && this.editingId() !== id)
1807
+ this.closeEditor(false);
1808
+ this.selectedId.set(id);
1809
+ }
1810
+ /** Lleva a la anotación (desde la lista del panel) y le da el foco. */
1811
+ revealAnnotation(annotation) {
1812
+ this.goToPage(annotation.page);
1813
+ this.selectedId.set(annotation.id);
1814
+ afterNextRender(() => this.focusMark(annotation.id, true), { injector: this.injector });
1815
+ }
1816
+ focusMark(id, scroll = false) {
1817
+ const el = this.host.nativeElement.querySelector(`.cb-pdf__mark[data-id="${id}"]`);
1818
+ if (!el)
1819
+ return;
1820
+ el.focus({ preventScroll: !scroll });
1821
+ if (scroll)
1822
+ el.scrollIntoView?.({ block: 'center', inline: 'nearest' });
1823
+ }
1824
+ openEditor(id) {
1825
+ const annotation = this.annotations().find((a) => a.id === id);
1826
+ if (!annotation || (annotation.type !== 'note' && annotation.type !== 'highlight'))
1827
+ return;
1828
+ this.selectedId.set(id);
1829
+ this.editingId.set(id);
1830
+ afterNextRender(() => {
1831
+ const textarea = this.cardText()?.nativeElement;
1832
+ if (!textarea)
1833
+ return;
1834
+ textarea.value = annotation.type === 'note' ? annotation.text : (annotation.comment ?? '');
1835
+ textarea.focus();
1836
+ }, { injector: this.injector });
1837
+ }
1838
+ onCardInput(value) {
1839
+ const annotation = this.annotations().find((a) => a.id === this.editingId());
1840
+ if (annotation?.type === 'note')
1841
+ this.replaceAnnotation({ ...annotation, text: value });
1842
+ else if (annotation?.type === 'highlight')
1843
+ this.replaceAnnotation({ ...annotation, comment: value || undefined });
1844
+ }
1845
+ onCardKeydown(event) {
1846
+ event.stopPropagation();
1847
+ if (event.key === 'Escape' || (event.key === 'Enter' && (event.ctrlKey || event.metaKey))) {
1848
+ event.preventDefault();
1849
+ this.closeEditor(true);
1850
+ }
1851
+ }
1852
+ /** Cierra el editor: una nota vacía se borra y una con texto ajusta su caja a lo escrito. */
1853
+ closeEditor(refocus) {
1854
+ const id = this.editingId();
1855
+ if (!id)
1856
+ return;
1857
+ this.editingId.set(null);
1858
+ const annotation = this.annotations().find((a) => a.id === id);
1859
+ if (annotation?.type === 'note') {
1860
+ if (!annotation.text.trim()) {
1861
+ this.annotations.update((list) => list.filter((a) => a.id !== id));
1862
+ this.selectedId.set(null);
1863
+ if (refocus)
1864
+ this.viewport()?.nativeElement.focus();
1865
+ return;
1866
+ }
1867
+ this.measureNote(annotation);
1868
+ }
1869
+ if (refocus)
1870
+ afterNextRender(() => this.focusMark(id), { injector: this.injector });
1871
+ }
1872
+ /** La caja de la nota en el PDF es la que ocupa su texto en pantalla. */
1873
+ measureNote(note) {
1874
+ const el = this.host.nativeElement.querySelector(`.cb-pdf__mark[data-id="${note.id}"]`);
1875
+ const size = this.sizes()[note.page - 1];
1876
+ if (!el || !size || !el.offsetWidth)
1877
+ return;
1878
+ const rect = viewToRect(this.geomOf(size), this.viewOf(size), el.offsetLeft, el.offsetTop, el.offsetWidth, el.offsetHeight);
1879
+ if (rect.some((v, i) => Math.abs(v - note.rect[i]) > 0.5))
1880
+ this.replaceAnnotation({ ...note, rect });
1881
+ }
1882
+ onMarkPointerDown(event, annotation) {
1883
+ event.stopPropagation();
1884
+ const el = event.currentTarget;
1885
+ if (!this.annotatable() || event.button !== 0)
1886
+ return;
1887
+ if (annotation.type === 'highlight') {
1888
+ this.select(annotation.id);
1889
+ return;
1890
+ }
1891
+ // Sin el default no empieza una selección de texto al arrastrar; el foco se da a mano.
1892
+ event.preventDefault();
1893
+ el.focus({ preventScroll: true });
1894
+ this.trackPointer(event, el, (dx, dy) => this.translated(annotation, dx, dy), () => {
1895
+ if (annotation.type === 'note' && this.editingId() !== annotation.id)
1896
+ this.openEditor(annotation.id);
1897
+ });
1898
+ }
1899
+ onResizePointerDown(event, annotation) {
1900
+ event.stopPropagation();
1901
+ event.preventDefault();
1902
+ if (annotation.type !== 'signature')
1903
+ return;
1904
+ const size = this.sizes()[annotation.page - 1];
1905
+ if (!size)
1906
+ return;
1907
+ const width = rectToView(this.geomOf(size), this.viewOf(size), annotation.rect).width;
1908
+ this.trackPointer(event, event.currentTarget, (dx) => ({ ...annotation, rect: scaleRect(annotation.rect, (width + dx) / Math.max(1, width)) }));
1909
+ }
1910
+ /** Arrastre con vista previa: se confirma al soltar; sin moverse es un clic (`onClick`). */
1911
+ trackPointer(event, el, update, onClick) {
1912
+ el.setPointerCapture?.(event.pointerId);
1913
+ const [x, y] = [event.clientX, event.clientY];
1914
+ let moved = false;
1915
+ const move = (e) => {
1916
+ const [dx, dy] = [e.clientX - x, e.clientY - y];
1917
+ if (!moved && Math.hypot(dx, dy) < 3)
1918
+ return;
1919
+ moved = true;
1920
+ this.dragPreview.set(update(dx, dy));
1921
+ };
1922
+ const end = () => {
1923
+ el.removeEventListener('pointermove', move);
1924
+ el.removeEventListener('pointerup', end);
1925
+ el.removeEventListener('pointercancel', end);
1926
+ const preview = this.dragPreview();
1927
+ this.dragPreview.set(null);
1928
+ if (moved && preview)
1929
+ this.replaceAnnotation(preview);
1930
+ else
1931
+ onClick?.();
1932
+ };
1933
+ el.addEventListener('pointermove', move);
1934
+ el.addEventListener('pointerup', end);
1935
+ el.addEventListener('pointercancel', end);
1936
+ }
1937
+ onMarkKeydown(event, rendered) {
1938
+ if (!this.annotatable() || event.ctrlKey || event.metaKey || event.altKey)
1939
+ return;
1940
+ // La de la plantilla puede ser de antes del último repintado (tecla mantenida): se usa la actual.
1941
+ const annotation = this.annotations().find((a) => a.id === rendered.id) ?? rendered;
1942
+ const step = event.shiftKey ? 20 : 4;
1943
+ const arrows = { ArrowLeft: [-step, 0], ArrowRight: [step, 0], ArrowUp: [0, -step], ArrowDown: [0, step] };
1944
+ if (event.key === 'Delete' || event.key === 'Backspace') {
1945
+ this.removeAnnotation(annotation);
1946
+ }
1947
+ else if (event.key === 'Enter' || event.key === ' ') {
1948
+ if (annotation.type !== 'note' && annotation.type !== 'highlight')
1949
+ return;
1950
+ this.openEditor(annotation.id);
1951
+ }
1952
+ else if (arrows[event.key] && annotation.type !== 'highlight') {
1953
+ const moved = this.translated(annotation, ...arrows[event.key]);
1954
+ this.replaceAnnotation(moved);
1955
+ this.announcePosition(moved);
1956
+ }
1957
+ else if ((event.key === '+' || event.key === '=' || event.key === '-') && (annotation.type === 'signature' || annotation.type === 'note')) {
1958
+ const grow = event.key !== '-';
1959
+ if (annotation.type === 'signature') {
1960
+ const rect = scaleRect(annotation.rect, grow ? 1.1 : 1 / 1.1);
1961
+ const pageWidth = (this.sizes()[annotation.page - 1]?.width ?? 1) / (this.sizes()[annotation.page - 1]?.userUnit ?? 1);
1962
+ this.replaceAnnotation({ ...annotation, rect });
1963
+ this.announce(cbFormat(this.t().pdfSignatureResized, Math.round(((rect[2] - rect[0]) / pageWidth) * 100)));
1964
+ }
1965
+ else {
1966
+ const fontSize = clamp((annotation.fontSize ?? 12) + (grow ? 1 : -1), 6, 48);
1967
+ this.replaceAnnotation({ ...annotation, fontSize });
1968
+ this.announce(cbFormat(this.t().pdfNoteResized, fontSize));
1969
+ afterNextRender(() => this.measureNote({ ...annotation, fontSize }), { injector: this.injector });
1970
+ }
1971
+ }
1972
+ else {
1973
+ return;
1974
+ }
1975
+ event.preventDefault();
1976
+ event.stopPropagation();
1977
+ }
1978
+ /** Desplaza `dx`, `dy` píxeles de pantalla (con el giro, en el sentido en que se ven). */
1979
+ translated(annotation, dx, dy) {
1980
+ const size = this.sizes()[annotation.page - 1];
1981
+ if (!size || annotation.type === 'highlight')
1982
+ return annotation;
1983
+ const geom = this.geomOf(size);
1984
+ const view = this.viewOf(size);
1985
+ const [ax, ay] = viewToPdf(geom, view, 0, 0);
1986
+ const [bx, by] = viewToPdf(geom, view, dx, dy);
1987
+ const [px, py] = [bx - ax, by - ay];
1988
+ if (annotation.type === 'ink') {
1989
+ return { ...annotation, paths: annotation.paths.map((p) => p.map((v, i) => Math.round((v + (i % 2 ? py : px)) * 100) / 100)) };
1990
+ }
1991
+ return { ...annotation, rect: moveRect(geom, annotation.rect, px, py) };
1992
+ }
1993
+ announcePosition(annotation) {
1994
+ const box = this.marks()[annotation.page - 1]?.find((m) => m.a.id === annotation.id)?.box;
1995
+ const page = this.pageBoxes()[annotation.page - 1];
1996
+ if (!box || !page)
1997
+ return;
1998
+ this.announce(cbFormat(this.t().pdfAnnotationMoved, Math.round((box.left / page.width) * 100), Math.round((box.top / page.height) * 100)));
1999
+ }
2000
+ announce(text) {
2001
+ // Un espacio duro alterno hace que se repita aunque el texto sea el mismo.
2002
+ this.announceFlip = !this.announceFlip;
2003
+ this.announcement.set(this.announceFlip ? text : `${text} `);
2004
+ }
2005
+ /** Centro de la parte visible de la página `n`, en píxeles de la página. */
2006
+ visibleSpot(n) {
2007
+ const root = this.viewport()?.nativeElement;
2008
+ const el = this.pageEl(n);
2009
+ if (!root || !el)
2010
+ return null;
2011
+ const top = Math.max(0, root.scrollTop - el.offsetTop);
2012
+ const bottom = Math.min(el.offsetHeight, root.scrollTop + root.clientHeight - el.offsetTop);
2013
+ const y = bottom > top ? (top + bottom) / 2 : el.offsetHeight / 3;
2014
+ return { x: (el.offsetWidth || this.pageBoxes()[n - 1]?.width || 0) / 2, y };
2015
+ }
2016
+ toMark(a, size) {
2017
+ const geom = this.geomOf(size);
2018
+ const view = this.viewOf(size);
2019
+ const color = CB_PDF_COLORS[a.type === 'signature' ? 'blue' : a.color];
2020
+ const base = { a, light: color.light, dark: color.dark, label: this.labelOf(a) };
2021
+ switch (a.type) {
2022
+ case 'highlight': {
2023
+ const boxes = a.rects.map((r) => rectToView(geom, view, r));
2024
+ const box = unionBox(boxes);
2025
+ return { ...base, box, rects: boxes.map((b) => ({ ...b, left: b.left - box.left, top: b.top - box.top })) };
2026
+ }
2027
+ case 'note':
2028
+ return { ...base, box: rectToView(geom, view, a.rect), fontPx: (a.fontSize ?? 12) * view.factor * geom.userUnit };
2029
+ case 'ink': {
2030
+ const pad = (a.thickness / 2 + 1) * view.factor;
2031
+ const points = a.paths.map((p) => {
2032
+ const out = [];
2033
+ for (let i = 0; i + 1 < p.length; i += 2)
2034
+ out.push(pdfToView(geom, view, p[i], p[i + 1]));
2035
+ return out;
2036
+ });
2037
+ const flat = points.flat();
2038
+ const left = Math.min(...flat.map((p) => p[0])) - pad;
2039
+ const top = Math.min(...flat.map((p) => p[1])) - pad;
2040
+ const width = Math.max(...flat.map((p) => p[0])) + pad - left;
2041
+ const height = Math.max(...flat.map((p) => p[1])) + pad - top;
2042
+ const d = points.map((p) => p.map(([x, y], i) => `${i ? 'L' : 'M'}${round2(x - left)} ${round2(y - top)}`).join(' ')).join(' ');
2043
+ return { ...base, box: { left, top, width, height }, d, strokePx: a.thickness * view.factor };
2044
+ }
2045
+ case 'signature':
2046
+ return { ...base, box: rectToView(geom, view, a.rect) };
2047
+ }
2048
+ }
2049
+ labelOf(a) {
2050
+ const t = this.t();
2051
+ switch (a.type) {
2052
+ case 'highlight': {
2053
+ const label = cbFormat(t.pdfHighlightOf, truncate(a.text, 80));
2054
+ return a.comment ? `${label}. ${t.pdfComment}: ${truncate(a.comment, 120)}` : label;
2055
+ }
2056
+ case 'note':
2057
+ return cbFormat(t.pdfNoteOf, a.text.trim() ? truncate(a.text, 120) : t.pdfEmptyNote);
2058
+ case 'ink':
2059
+ return t.pdfInkOf;
2060
+ case 'signature':
2061
+ return t.pdfSignatureOf;
2062
+ }
2063
+ }
2064
+ nounOf(a) {
2065
+ const t = this.t();
2066
+ return a.type === 'highlight' ? t.pdfHighlightNoun : a.type === 'note' ? t.pdfNote : a.type === 'ink' ? t.pdfInkOf : t.pdfSignatureOf;
2067
+ }
2068
+ swatchOf(a) {
2069
+ if (a.type === 'signature')
2070
+ return CB_PDF_COLORS.blue.dark;
2071
+ return a.type === 'highlight' ? CB_PDF_COLORS[a.color].light : CB_PDF_COLORS[a.color].dark;
2072
+ }
2073
+ colorLabel(color) {
2074
+ const t = this.t();
2075
+ return { yellow: t.pdfColorYellow, green: t.pdfColorGreen, blue: t.pdfColorBlue, red: t.pdfColorRed }[color];
2076
+ }
2077
+ /** Posición del editor: bajo la anotación, sin salirse de la página. */
2078
+ cardLeft(m, pageWidth) {
2079
+ return clamp(m.box.left, 0, Math.max(0, pageWidth - 260));
2080
+ }
2081
+ noteHeight(m) {
2082
+ if (m.a.type !== 'note')
2083
+ return m.box.height;
2084
+ const lines = Math.max(1, m.a.text.split('\n').length);
2085
+ return Math.max(m.box.height, lines * (m.fontPx ?? 16) * 1.25 + 8);
2086
+ }
2087
+ newBase(page) {
2088
+ const author = this.author();
2089
+ return { id: `cb-annot-${Date.now().toString(36)}-${++nextAnnotationId}`, page, ...(author ? { author } : {}), date: new Date().toISOString() };
2090
+ }
2091
+ geomOf(size) {
2092
+ return { x0: size.x0, y0: size.y0, width: size.width, height: size.height, userUnit: size.userUnit };
2093
+ }
2094
+ viewOf(size) {
2095
+ return { factor: this.scale() * CSS_UNITS, rotation: (size.rotate + this.rotation()) % 360 };
2096
+ }
2097
+ /** Altura (para ordenar de arriba abajo en el panel). */
2098
+ topOf(a) {
2099
+ if (a.type === 'highlight')
2100
+ return Math.max(...a.rects.map((r) => r[3]));
2101
+ if (a.type === 'ink')
2102
+ return Math.max(...a.paths.flatMap((p) => p.filter((_, i) => i % 2)));
2103
+ return a.rect[3];
2104
+ }
2105
+ /**
2106
+ * Escribe las anotaciones en el `annotationStorage` de pdf.js mientras dura `run` (guardar o
2107
+ * tomar la copia para imprimir) y las quita después, sin marcar el formulario como cambiado.
2108
+ */
2109
+ async withAnnotations(run) {
2110
+ const pdf = this.pdf;
2111
+ const list = this.annotations();
2112
+ if (!pdf || !list.length)
2113
+ return run();
2114
+ const storage = pdf.annotationStorage;
2115
+ const hook = storage.onSetModified;
2116
+ storage.onSetModified = null;
2117
+ const keys = [];
2118
+ try {
2119
+ for (const a of list) {
2120
+ const size = this.sizes()[a.page - 1];
2121
+ if (!size)
2122
+ continue;
2123
+ const bitmap = a.type === 'signature' ? await imageToBitmap(a.image) : undefined;
2124
+ const value = toPdfJsAnnotation(a, size.rotate, bitmap);
2125
+ if (!value)
2126
+ continue;
2127
+ const key = `${PDFJS_EDITOR_PREFIX}${a.id}`;
2128
+ storage.setValue(key, value);
2129
+ keys.push(key);
2130
+ }
2131
+ return await run();
2132
+ }
2133
+ finally {
2134
+ for (const key of keys)
2135
+ storage.remove(key);
2136
+ storage.onSetModified = hook;
2137
+ }
2138
+ }
979
2139
  // --- Barra y teclado -------------------------------------------------------------
2140
+ isCollapsed(path) {
2141
+ return this.collapsed().has(path);
2142
+ }
2143
+ toggleOutline(path) {
2144
+ this.collapsed.update((set) => {
2145
+ const next = new Set(set);
2146
+ if (next.has(path))
2147
+ next.delete(path);
2148
+ else
2149
+ next.add(path);
2150
+ return next;
2151
+ });
2152
+ }
2153
+ openOutlineItem(item) {
2154
+ if (item.dest)
2155
+ void this.goToDestination(item.dest);
2156
+ else if (item.url && this.externalLinks() && SAFE_URL.test(item.url))
2157
+ globalThis.open?.(item.url, '_blank', 'noopener,noreferrer');
2158
+ }
2159
+ /** Acciones con nombre de los enlaces (NextPage, PrevPage, FirstPage, LastPage). */
2160
+ namedAction(action) {
2161
+ if (action === 'NextPage')
2162
+ this.nextPage();
2163
+ else if (action === 'PrevPage')
2164
+ this.previousPage();
2165
+ else if (action === 'FirstPage')
2166
+ this.goToPage(1);
2167
+ else if (action === 'LastPage')
2168
+ this.goToPage(this.numPages());
2169
+ }
2170
+ onFormInput() {
2171
+ this.formChange.emit();
2172
+ }
980
2173
  onPageInput(event) {
981
2174
  const input = event.target;
982
2175
  const value = Number.parseInt(input.value, 10);
@@ -995,6 +2188,11 @@ class CbPdfViewer {
995
2188
  this.thumbEls()[next - 1]?.nativeElement.focus();
996
2189
  }
997
2190
  onHostKeydown(event) {
2191
+ if (event.key === 'Escape' && this.tool() !== 'none') {
2192
+ this.tool.set('none');
2193
+ event.preventDefault();
2194
+ return;
2195
+ }
998
2196
  if (this.state() !== 'ready' || !(event.ctrlKey || event.metaKey) || event.altKey)
999
2197
  return;
1000
2198
  const key = event.key.toLowerCase();
@@ -1045,7 +2243,7 @@ class CbPdfViewer {
1045
2243
  return (size.rotate + this.rotation()) % 180 === 0 ? [size.width, size.height] : [size.height, size.width];
1046
2244
  }
1047
2245
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbPdfViewer, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1048
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbPdfViewer, isStandalone: true, selector: "cb-pdf-viewer", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, fileName: { classPropertyName: "fileName", publicName: "fileName", isSignal: true, isRequired: false, transformFunction: null }, password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null }, withCredentials: { classPropertyName: "withCredentials", publicName: "withCredentials", isSignal: true, isRequired: false, transformFunction: null }, httpHeaders: { classPropertyName: "httpHeaders", publicName: "httpHeaders", isSignal: true, isRequired: false, transformFunction: null }, page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, rotation: { classPropertyName: "rotation", publicName: "rotation", isSignal: true, isRequired: false, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showThumbnails: { classPropertyName: "showThumbnails", publicName: "showThumbnails", isSignal: true, isRequired: false, transformFunction: null }, showRotate: { classPropertyName: "showRotate", publicName: "showRotate", isSignal: true, isRequired: false, transformFunction: null }, downloadable: { classPropertyName: "downloadable", publicName: "downloadable", isSignal: true, isRequired: false, transformFunction: null }, printable: { classPropertyName: "printable", publicName: "printable", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { page: "pageChange", zoom: "zoomChange", rotation: "rotationChange", sidebar: "sidebarChange", loaded: "loaded", loadError: "loadError" }, host: { attributes: { "role": "region" }, listeners: { "keydown": "onHostKeydown($event)" }, properties: { "attr.aria-label": "ariaLabel() || t().pdfViewer" }, classAttribute: "cb-pdf-viewer" }, viewQueries: [{ propertyName: "viewport", first: true, predicate: ["viewport"], descendants: true, isSignal: true }, { propertyName: "pageEls", predicate: ["pageEl"], descendants: true, isSignal: true }, { propertyName: "thumbEls", predicate: ["thumbEl"], descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "passwordInput", first: true, predicate: ["passwordInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
2246
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbPdfViewer, isStandalone: true, selector: "cb-pdf-viewer", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, fileName: { classPropertyName: "fileName", publicName: "fileName", isSignal: true, isRequired: false, transformFunction: null }, password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null }, withCredentials: { classPropertyName: "withCredentials", publicName: "withCredentials", isSignal: true, isRequired: false, transformFunction: null }, httpHeaders: { classPropertyName: "httpHeaders", publicName: "httpHeaders", isSignal: true, isRequired: false, transformFunction: null }, page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, zoom: { classPropertyName: "zoom", publicName: "zoom", isSignal: true, isRequired: false, transformFunction: null }, rotation: { classPropertyName: "rotation", publicName: "rotation", isSignal: true, isRequired: false, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showSearch: { classPropertyName: "showSearch", publicName: "showSearch", isSignal: true, isRequired: false, transformFunction: null }, showThumbnails: { classPropertyName: "showThumbnails", publicName: "showThumbnails", isSignal: true, isRequired: false, transformFunction: null }, showRotate: { classPropertyName: "showRotate", publicName: "showRotate", isSignal: true, isRequired: false, transformFunction: null }, downloadable: { classPropertyName: "downloadable", publicName: "downloadable", isSignal: true, isRequired: false, transformFunction: null }, printable: { classPropertyName: "printable", publicName: "printable", isSignal: true, isRequired: false, transformFunction: null }, showOutline: { classPropertyName: "showOutline", publicName: "showOutline", isSignal: true, isRequired: false, transformFunction: null }, forms: { classPropertyName: "forms", publicName: "forms", isSignal: true, isRequired: false, transformFunction: null }, externalLinks: { classPropertyName: "externalLinks", publicName: "externalLinks", isSignal: true, isRequired: false, transformFunction: null }, sidebarView: { classPropertyName: "sidebarView", publicName: "sidebarView", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, annotatable: { classPropertyName: "annotatable", publicName: "annotatable", isSignal: true, isRequired: false, transformFunction: null }, annotations: { classPropertyName: "annotations", publicName: "annotations", isSignal: true, isRequired: false, transformFunction: null }, tool: { classPropertyName: "tool", publicName: "tool", isSignal: true, isRequired: false, transformFunction: null }, annotationColor: { classPropertyName: "annotationColor", publicName: "annotationColor", isSignal: true, isRequired: false, transformFunction: null }, author: { classPropertyName: "author", publicName: "author", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { page: "pageChange", zoom: "zoomChange", rotation: "rotationChange", sidebar: "sidebarChange", sidebarView: "sidebarViewChange", annotations: "annotationsChange", tool: "toolChange", annotationColor: "annotationColorChange", loaded: "loaded", loadError: "loadError", formChange: "formChange", annotationAdd: "annotationAdd", annotationRemove: "annotationRemove" }, host: { attributes: { "role": "region" }, listeners: { "keydown": "onHostKeydown($event)" }, properties: { "attr.aria-label": "ariaLabel() || t().pdfViewer" }, classAttribute: "cb-pdf-viewer" }, viewQueries: [{ propertyName: "viewport", first: true, predicate: ["viewport"], descendants: true, isSignal: true }, { propertyName: "pageEls", predicate: ["pageEl"], descendants: true, isSignal: true }, { propertyName: "thumbEls", predicate: ["thumbEl"], descendants: true, isSignal: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "passwordInput", first: true, predicate: ["passwordInput"], descendants: true, isSignal: true }, { propertyName: "cardText", first: true, predicate: ["cardText"], descendants: true, isSignal: true }], ngImport: i0, template: `
1049
2247
  @if (showToolbar()) {
1050
2248
  <div class="cb-pdf__toolbar">
1051
2249
  @if (showThumbnails()) {
@@ -1128,6 +2326,70 @@ class CbPdfViewer {
1128
2326
  }
1129
2327
  </div>
1130
2328
 
2329
+ @if (annotatable()) {
2330
+ <div class="cb-pdf__group" role="group" [attr.aria-label]="t().pdfAnnotate">
2331
+ <!-- mousedown sin default: pulsar la herramienta no quita la selección de texto. -->
2332
+ <button
2333
+ type="button"
2334
+ class="cb-pdf__tool"
2335
+ [attr.aria-label]="t().pdfHighlight"
2336
+ [attr.aria-pressed]="tool() === 'highlight'"
2337
+ [attr.title]="t().pdfHighlight"
2338
+ [disabled]="state() !== 'ready'"
2339
+ (mousedown)="$event.preventDefault()"
2340
+ (click)="onHighlightTool()"
2341
+ >
2342
+ <cb-icon name="highlighter" [size]="18" aria-hidden="true" />
2343
+ </button>
2344
+ <button
2345
+ type="button"
2346
+ class="cb-pdf__tool"
2347
+ [attr.aria-label]="t().pdfNote"
2348
+ [attr.aria-pressed]="tool() === 'note'"
2349
+ [attr.title]="t().pdfNote"
2350
+ [disabled]="state() !== 'ready'"
2351
+ (click)="onNoteTool($event)"
2352
+ >
2353
+ <cb-icon name="sticky-note" [size]="18" aria-hidden="true" />
2354
+ </button>
2355
+ <button
2356
+ type="button"
2357
+ class="cb-pdf__tool"
2358
+ [attr.aria-label]="t().pdfDraw"
2359
+ [attr.aria-pressed]="tool() === 'ink'"
2360
+ [attr.title]="t().pdfDraw"
2361
+ [disabled]="state() !== 'ready'"
2362
+ (click)="toggleTool('ink')"
2363
+ >
2364
+ <cb-icon name="pen-line" [size]="18" aria-hidden="true" />
2365
+ </button>
2366
+ <button
2367
+ type="button"
2368
+ class="cb-pdf__tool"
2369
+ aria-haspopup="dialog"
2370
+ [attr.aria-label]="t().pdfSign"
2371
+ [attr.title]="t().pdfSign"
2372
+ [disabled]="state() !== 'ready'"
2373
+ (click)="openSignature()"
2374
+ >
2375
+ <cb-icon name="signature" [size]="18" aria-hidden="true" />
2376
+ </button>
2377
+ <span class="cb-pdf__color">
2378
+ <span class="cb-pdf__swatch" aria-hidden="true" [style.background]="colors[annotationColor()].light" [style.border-color]="colors[annotationColor()].dark"></span>
2379
+ <select
2380
+ class="cb-pdf__field cb-pdf__color-select"
2381
+ [attr.aria-label]="t().pdfColor"
2382
+ [disabled]="state() !== 'ready'"
2383
+ (change)="annotationColor.set($any($event.target).value)"
2384
+ >
2385
+ @for (c of colorNames; track c) {
2386
+ <option [value]="c" [selected]="annotationColor() === c">{{ colorLabel(c) }}</option>
2387
+ }
2388
+ </select>
2389
+ </span>
2390
+ </div>
2391
+ }
2392
+
1131
2393
  <span class="cb-pdf__spacer"></span>
1132
2394
 
1133
2395
  @if (showSearch()) {
@@ -1158,7 +2420,14 @@ class CbPdfViewer {
1158
2420
  @if (downloadable() || printable()) {
1159
2421
  <div class="cb-pdf__group">
1160
2422
  @if (downloadable()) {
1161
- <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().download" [disabled]="state() !== 'ready'" (click)="download()">
2423
+ <button
2424
+ type="button"
2425
+ class="cb-pdf__tool"
2426
+ [attr.aria-label]="annotations().length ? t().pdfDownloadAnnotated : formModified() ? t().pdfDownloadFilled : t().download"
2427
+ [attr.title]="annotations().length ? t().pdfDownloadAnnotated : formModified() ? t().pdfDownloadFilled : null"
2428
+ [disabled]="state() !== 'ready'"
2429
+ (click)="download()"
2430
+ >
1162
2431
  <cb-icon name="download" [size]="18" aria-hidden="true" />
1163
2432
  </button>
1164
2433
  }
@@ -1180,7 +2449,51 @@ class CbPdfViewer {
1180
2449
 
1181
2450
  <div class="cb-pdf__body">
1182
2451
  @if (state() === 'ready' && sidebar() && showThumbnails()) {
1183
- <nav class="cb-pdf__sidebar" [id]="uid + '-thumbs'" [attr.aria-label]="t().thumbnails">
2452
+ <div
2453
+ class="cb-pdf__sidebar"
2454
+ [class.cb-pdf__sidebar--tabs]="sidebarTabs().length > 1"
2455
+ [class.cb-pdf__sidebar--outline]="activeView() !== 'thumbnails' || sidebarTabs().length > 2"
2456
+ [id]="uid + '-thumbs'"
2457
+ >
2458
+ @if (sidebarTabs().length > 1) {
2459
+ <div class="cb-pdf__views" role="group" [attr.aria-label]="t().pdfSidebarView">
2460
+ @for (view of sidebarTabs(); track view) {
2461
+ <button type="button" class="cb-pdf__view" [attr.aria-pressed]="activeView() === view" (click)="sidebarView.set(view)">
2462
+ {{ view === 'outline' ? t().pdfOutline : view === 'annotations' ? t().pdfAnnotations : t().thumbnails }}
2463
+ </button>
2464
+ }
2465
+ </div>
2466
+ }
2467
+ @if (activeView() === 'outline') {
2468
+ <nav class="cb-pdf__outline" [attr.aria-label]="t().pdfOutline">
2469
+ <ng-container *ngTemplateOutlet="outlineList; context: { $implicit: outline(), path: '' }" />
2470
+ </nav>
2471
+ } @else if (activeView() === 'annotations') {
2472
+ <section class="cb-pdf__notes-panel" [attr.aria-label]="t().pdfAnnotations">
2473
+ @if (!sortedAnnotations().length) {
2474
+ <p class="cb-pdf__notes-empty">{{ t().pdfNoAnnotations }}</p>
2475
+ } @else {
2476
+ <ul class="cb-pdf__notes-list">
2477
+ @for (a of sortedAnnotations(); track a.id) {
2478
+ <li class="cb-pdf__notes-item">
2479
+ <button type="button" class="cb-pdf__notes-link" (click)="revealAnnotation(a)">
2480
+ <span class="cb-pdf__notes-swatch" aria-hidden="true" [style.background]="swatchOf(a)"></span>
2481
+ <span class="cb-pdf__notes-text">
2482
+ <!-- La coma oculta separa la página del texto en el nombre del botón. -->
2483
+ <span class="cb-pdf__notes-page">{{ format(t().pageOf, a.page, numPages()) }}<span class="cb-pdf__sr">, </span></span>
2484
+ <span class="cb-pdf__notes-label">{{ labelOf(a) }}</span>
2485
+ </span>
2486
+ </button>
2487
+ <button type="button" class="cb-pdf__notes-remove" [attr.aria-label]="t().pdfDeleteAnnotation + ': ' + labelOf(a)" (click)="removeAnnotation(a)">
2488
+ <cb-icon name="trash" [size]="14" aria-hidden="true" />
2489
+ </button>
2490
+ </li>
2491
+ }
2492
+ </ul>
2493
+ }
2494
+ </section>
2495
+ } @else {
2496
+ <nav class="cb-pdf__thumbs" [attr.aria-label]="t().thumbnails">
1184
2497
  @for (thumb of thumbBoxes(); track $index; let i = $index) {
1185
2498
  <button
1186
2499
  #thumbEl
@@ -1197,7 +2510,9 @@ class CbPdfViewer {
1197
2510
  <span class="cb-pdf__thumb-label" aria-hidden="true">{{ i + 1 }}</span>
1198
2511
  </button>
1199
2512
  }
1200
- </nav>
2513
+ </nav>
2514
+ }
2515
+ </div>
1201
2516
  }
1202
2517
 
1203
2518
  <div
@@ -1243,7 +2558,10 @@ class CbPdfViewer {
1243
2558
  </form>
1244
2559
  }
1245
2560
  @case ('ready') {
1246
- <div class="cb-pdf__pages">
2561
+ @if (toolHint(); as hint) {
2562
+ <div class="cb-pdf__hint">{{ hint }}</div>
2563
+ }
2564
+ <div class="cb-pdf__pages" (pointerup)="onPagesPointerUp()">
1247
2565
  @for (box of pageBoxes(); track $index; let i = $index) {
1248
2566
  <div
1249
2567
  #pageEl
@@ -1258,6 +2576,97 @@ class CbPdfViewer {
1258
2576
  >
1259
2577
  <div class="cb-pdf__canvas"></div>
1260
2578
  <div class="cb-pdf__text"></div>
2579
+ <div class="cb-pdf__annotations" (input)="onFormInput()" (change)="onFormInput()"></div>
2580
+ @if (annotatable() || marks()[i]?.length) {
2581
+ <div
2582
+ class="cb-pdf__marks"
2583
+ [class.cb-pdf__marks--capture]="annotatable() && (tool() === 'ink' || tool() === 'note')"
2584
+ [class.cb-pdf__marks--ink]="tool() === 'ink'"
2585
+ [class.cb-pdf__marks--pass]="tool() === 'highlight'"
2586
+ (pointerdown)="onMarksPointerDown($event, i + 1)"
2587
+ >
2588
+ @for (m of marks()[i]; track m.a.id) {
2589
+ <div
2590
+ class="cb-pdf__mark"
2591
+ [class]="'cb-pdf__mark cb-pdf__mark--' + m.a.type"
2592
+ [class.cb-is-selected]="selectedId() === m.a.id"
2593
+ [attr.data-id]="m.a.id"
2594
+ [attr.tabindex]="annotatable() ? 0 : null"
2595
+ [attr.role]="annotatable() ? 'button' : 'img'"
2596
+ [attr.aria-label]="m.label"
2597
+ [attr.aria-describedby]="annotatable() ? uid + '-mark-hint' : null"
2598
+ [attr.aria-expanded]="annotatable() && (m.a.type === 'note' || m.a.type === 'highlight') ? editingId() === m.a.id : null"
2599
+ [style.left.px]="m.box.left"
2600
+ [style.top.px]="m.box.top"
2601
+ [style.width.px]="m.a.type === 'note' ? null : m.box.width"
2602
+ [style.height.px]="m.a.type === 'note' ? null : m.box.height"
2603
+ [style.--cb-pdf-mark-light]="m.light"
2604
+ [style.--cb-pdf-mark-dark]="m.dark"
2605
+ (pointerdown)="onMarkPointerDown($event, m.a)"
2606
+ (keydown)="onMarkKeydown($event, m.a)"
2607
+ (focus)="select(m.a.id)"
2608
+ >
2609
+ @switch (m.a.type) {
2610
+ @case ('highlight') {
2611
+ @for (r of m.rects; track $index) {
2612
+ <span class="cb-pdf__hl-rect" [style.left.px]="r.left" [style.top.px]="r.top" [style.width.px]="r.width" [style.height.px]="r.height"></span>
2613
+ }
2614
+ @if (m.a.comment) {
2615
+ <span class="cb-pdf__hl-badge" aria-hidden="true"></span>
2616
+ }
2617
+ }
2618
+ @case ('note') {
2619
+ <span class="cb-pdf__note-text" [style.font-size.px]="m.fontPx" [style.min-width.px]="m.box.width">{{ m.a.text || ' ' }}</span>
2620
+ }
2621
+ @case ('ink') {
2622
+ <svg class="cb-pdf__ink" [attr.viewBox]="'0 0 ' + m.box.width + ' ' + m.box.height" aria-hidden="true">
2623
+ <path [attr.d]="m.d" [attr.stroke-width]="m.strokePx" />
2624
+ </svg>
2625
+ }
2626
+ @case ('signature') {
2627
+ <img class="cb-pdf__sig" [src]="m.a.image" alt="" draggable="false" />
2628
+ @if (annotatable() && selectedId() === m.a.id) {
2629
+ <span class="cb-pdf__resize" aria-hidden="true" (pointerdown)="onResizePointerDown($event, m.a)"></span>
2630
+ }
2631
+ }
2632
+ }
2633
+ </div>
2634
+ }
2635
+ @if (stroke(); as s) {
2636
+ @if (s.page === i + 1) {
2637
+ <svg class="cb-pdf__stroke" aria-hidden="true" [style.--cb-pdf-mark-dark]="colors[annotationColor()].dark">
2638
+ <polyline [attr.points]="s.points.join(' ')" [attr.stroke-width]="2 * box.scaleFactor" />
2639
+ </svg>
2640
+ }
2641
+ }
2642
+ @if (editingMark(); as e) {
2643
+ @if (e.a.page === i + 1) {
2644
+ <div
2645
+ class="cb-pdf__card"
2646
+ role="group"
2647
+ [attr.aria-label]="e.label"
2648
+ [style.left.px]="cardLeft(e, box.width)"
2649
+ [style.top.px]="e.box.top + (e.a.type === 'note' ? noteHeight(e) : e.box.height) + 6"
2650
+ (pointerdown)="$event.stopPropagation()"
2651
+ (keydown)="onCardKeydown($event)"
2652
+ >
2653
+ <label class="cb-pdf__card-label" [for]="uid + '-card'">{{ e.a.type === 'note' ? t().pdfNoteText : t().pdfComment }}</label>
2654
+ <textarea
2655
+ #cardText
2656
+ class="cb-pdf__field cb-pdf__card-text"
2657
+ rows="3"
2658
+ [id]="uid + '-card'"
2659
+ (input)="onCardInput($any($event.target).value)"
2660
+ ></textarea>
2661
+ <div class="cb-pdf__card-actions">
2662
+ <button type="button" class="cb-pdf__card-btn" (click)="removeAnnotation(e.a)">{{ t().pdfDeleteAnnotation }}</button>
2663
+ <button type="button" class="cb-pdf__card-btn cb-pdf__card-btn--primary" (click)="closeEditor(true)">{{ t().pdfDone }}</button>
2664
+ </div>
2665
+ </div>
2666
+ }
2667
+ }
2668
+ </div>
2669
+ }
1261
2670
  </div>
1262
2671
  }
1263
2672
  </div>
@@ -1266,14 +2675,60 @@ class CbPdfViewer {
1266
2675
  </div>
1267
2676
  </div>
1268
2677
 
2678
+ @if (annotatable()) {
2679
+ <span class="cb-pdf__sr" [id]="uid + '-mark-hint'">{{ t().pdfAnnotationHint }}</span>
2680
+ <span class="cb-pdf__sr" aria-live="polite">{{ announcement() }}</span>
2681
+ <cb-dialog [(visible)]="signing" [header]="t().pdfSignTitle" width="34rem" (onHide)="onSignatureHide()">
2682
+ <cb-signature-pad [(value)]="signatureValue" background="transparent" [ariaLabel]="t().pdfSignTitle" />
2683
+ <div cb-dialog-footer class="cb-pdf__sign-actions">
2684
+ <button type="button" class="cb-pdf__card-btn" (click)="signing.set(false)">{{ t().pdfCancel }}</button>
2685
+ <button type="button" class="cb-pdf__card-btn cb-pdf__card-btn--primary" [disabled]="!signatureValue()" (click)="insertSignature()">
2686
+ {{ t().pdfInsertSignature }}
2687
+ </button>
2688
+ </div>
2689
+ </cb-dialog>
2690
+ }
2691
+
2692
+ <ng-template #outlineList let-items let-path="path">
2693
+ <ul class="cb-pdf__outline-list" [attr.role]="path ? 'group' : null">
2694
+ @for (item of items; track $index; let i = $index) {
2695
+ <li class="cb-pdf__outline-item">
2696
+ @if (item.items.length) {
2697
+ <button
2698
+ type="button"
2699
+ class="cb-pdf__outline-toggle"
2700
+ [attr.aria-expanded]="!isCollapsed(path + i)"
2701
+ [attr.aria-label]="format(isCollapsed(path + i) ? t().pdfExpandItem : t().pdfCollapseItem, item.title)"
2702
+ (click)="toggleOutline(path + i)"
2703
+ >
2704
+ <cb-icon [name]="isCollapsed(path + i) ? 'chevron-right' : 'chevron-down'" [size]="14" aria-hidden="true" />
2705
+ </button>
2706
+ }
2707
+ <button
2708
+ type="button"
2709
+ class="cb-pdf__outline-link"
2710
+ [class.cb-pdf__outline-link--bold]="item.bold"
2711
+ [class.cb-pdf__outline-link--italic]="item.italic"
2712
+ (click)="openOutlineItem(item)"
2713
+ >
2714
+ {{ item.title }}
2715
+ </button>
2716
+ @if (item.items.length && !isCollapsed(path + i)) {
2717
+ <ng-container *ngTemplateOutlet="outlineList; context: { $implicit: item.items, path: path + i + '.' }" />
2718
+ }
2719
+ </li>
2720
+ }
2721
+ </ul>
2722
+ </ng-template>
2723
+
1269
2724
  @if (printProgress(); as p) {
1270
2725
  <div class="cb-pdf__toast" role="status">{{ format(t().preparingPrint, p.done, p.total) }}</div>
1271
2726
  }
1272
- `, isInline: true, styles: [":host{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:var(--cb-pdf-viewer-height, 40rem);min-height:16rem;overflow:hidden;border:var(--cb-border-width, 1px) solid var(--cb-color-border);border-radius:var(--cb-radius-lg);background:var(--cb-color-surface);color:var(--cb-color-text);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm);container-type:inline-size}.cb-pdf__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--cb-space-2);padding:var(--cb-space-2) var(--cb-space-3);border-bottom:var(--cb-border-width, 1px) solid var(--cb-color-border);background:var(--cb-color-surface)}.cb-pdf__group{display:inline-flex;align-items:center;gap:var(--cb-space-1)}.cb-pdf__spacer{flex:1}.cb-pdf__tool{display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;padding:0;border:none;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text);cursor:pointer;transition:background-color var(--cb-duration-fast) var(--cb-ease)}.cb-pdf__tool:hover:not(:disabled){background:var(--cb-color-surface-hover)}.cb-pdf__tool[aria-expanded=true]{background:var(--cb-color-surface-hover);color:var(--cb-color-primary-600)}.cb-pdf__tool:disabled{opacity:var(--cb-opacity-disabled, .5);cursor:default}.cb-pdf__tool:focus-visible,.cb-pdf__field:focus-visible,.cb-pdf__thumb:focus-visible,.cb-pdf__submit:focus-visible,.cb-pdf__viewport:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-pdf__field{height:2.25rem;padding:0 var(--cb-space-2);border:var(--cb-border-width, 1px) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-md);background:var(--cb-color-surface);color:var(--cb-color-text);font:inherit}.cb-pdf__field:disabled{opacity:var(--cb-opacity-disabled, .5)}.cb-pdf__page-input{width:3.25rem;text-align:center}.cb-pdf__total{min-width:2.5rem;color:var(--cb-color-text-muted);white-space:nowrap}.cb-pdf__zoom{width:9.5rem}.cb-pdf__search{position:relative}.cb-pdf__search-icon{position:absolute;left:.625rem;color:var(--cb-color-text-muted);pointer-events:none}.cb-pdf__search-input{width:14rem;padding-left:2rem}.cb-pdf__status{min-width:4.5rem;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs);text-align:center;white-space:nowrap}@container (max-width: 40rem){.cb-pdf__zoom,.cb-pdf__status{display:none}.cb-pdf__search-input{width:9rem}}.cb-pdf__body{display:flex;flex:1;min-height:0}.cb-pdf__sidebar{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-3);flex:none;width:10rem;padding:var(--cb-space-3) 0;overflow-y:auto;border-right:var(--cb-border-width, 1px) solid var(--cb-color-border);background:var(--cb-color-surface)}.cb-pdf__thumb{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-1);padding:var(--cb-space-1);border:2px solid transparent;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text-muted);font:inherit;font-size:var(--cb-text-xs);cursor:pointer}.cb-pdf__thumb:hover{background:var(--cb-color-surface-hover)}.cb-pdf__thumb.cb-is-active{border-color:var(--cb-color-primary-500);color:var(--cb-color-text-strong);font-weight:var(--cb-font-weight-semibold)}.cb-pdf__thumb-canvas{display:block;background:#fff;box-shadow:0 1px 3px #0f172a33}:host ::ng-deep .cb-pdf__thumb-canvas canvas{display:block;width:100%;height:100%}.cb-pdf__viewport{position:relative;flex:1;min-width:0;overflow:auto;background:var(--cb-color-surface-subtle)}.cb-pdf__pages{display:flex;flex-direction:column;align-items:center;gap:16px;padding:16px;width:max-content;min-width:100%;box-sizing:border-box}.cb-pdf__page{--user-unit: 1;--total-scale-factor: calc(var(--scale-factor) * var(--user-unit));--scale-round-x: 1px;--scale-round-y: 1px;position:relative;flex:none;background:#fff;box-shadow:0 1px 3px #0f172a2e,0 4px 12px #0f172a14;direction:ltr}.cb-pdf__canvas,:host ::ng-deep .cb-pdf__canvas canvas{position:absolute;inset:0;width:100%;height:100%}.cb-pdf__text{position:absolute;inset:0;overflow:clip;opacity:1;line-height:1;text-align:initial;letter-spacing:normal;word-spacing:normal;-webkit-text-size-adjust:none;text-size-adjust:none;forced-color-adjust:none;transform-origin:0 0;caret-color:CanvasText;z-index:0;--min-font-size: 1;--text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size));--min-font-size-inv: calc(1 / var(--min-font-size))}.cb-pdf__text[data-main-rotation=\"90\"]{transform:rotate(90deg) translateY(-100%)}.cb-pdf__text[data-main-rotation=\"180\"]{transform:rotate(180deg) translate(-100%,-100%)}.cb-pdf__text[data-main-rotation=\"270\"]{transform:rotate(270deg) translate(-100%)}:host ::ng-deep .cb-pdf__text :is(span,br){position:absolute;color:transparent;white-space:pre;cursor:text;transform-origin:0% 0%;-webkit-user-select:text;user-select:text}:host ::ng-deep .cb-pdf__text>:not(.markedContent),:host ::ng-deep .cb-pdf__text .markedContent span:not(.markedContent){--font-height: 0;--scale-x: 1;--rotate: 0deg;z-index:1;font-size:calc(var(--text-scale-factor) * var(--font-height));transform:rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv))}:host ::ng-deep .cb-pdf__text .markedContent{display:contents}:host ::ng-deep .cb-pdf__text span[role=img]{-webkit-user-select:none;user-select:none;cursor:default}:host ::ng-deep .cb-pdf__text ::selection{background:color-mix(in srgb,var(--cb-color-primary-500) 35%,transparent);color:transparent}:host ::ng-deep .cb-pdf__text .cb-pdf__hl{position:relative;margin:-1px;padding:1px;border-radius:3px;background:#facc1573;font-size:inherit;transform:none}:host ::ng-deep .cb-pdf__text .cb-pdf__hl.cb-is-selected{background:#f973168c;outline:2px solid rgb(234 88 12)}@media(forced-colors:active){:host ::ng-deep .cb-pdf__text .cb-pdf__hl{background:transparent;outline:1px solid Highlight}:host ::ng-deep .cb-pdf__text .cb-pdf__hl.cb-is-selected{outline:3px solid Highlight}}.cb-pdf__message{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--cb-space-3);height:100%;padding:var(--cb-space-6);color:var(--cb-color-text-muted);text-align:center}.cb-pdf__message--error{color:var(--cb-color-danger)}.cb-pdf__password{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-2);max-width:24rem;margin:var(--cb-space-6) auto;padding:var(--cb-space-6);border:var(--cb-border-width, 1px) solid var(--cb-color-border);border-radius:var(--cb-radius-lg);background:var(--cb-color-surface);text-align:center}.cb-pdf__password-text{margin:0}.cb-pdf__password-label{align-self:flex-start;font-weight:var(--cb-font-weight-medium)}.cb-pdf__password-row{display:flex;gap:var(--cb-space-2);width:100%}.cb-pdf__password-row .cb-pdf__field{flex:1;min-width:0}.cb-pdf__password-error{margin:0;color:var(--cb-color-danger)}.cb-pdf__submit{height:2.25rem;padding:0 var(--cb-space-4);border:none;border-radius:var(--cb-radius-md);background:var(--cb-color-primary-500);color:var(--cb-color-primary-contrast);font:inherit;font-weight:var(--cb-font-weight-semibold);cursor:pointer}.cb-pdf__submit:hover{background:var(--cb-color-primary-600)}.cb-pdf__toast{position:absolute;left:50%;bottom:var(--cb-space-4);transform:translate(-50%);padding:var(--cb-space-2) var(--cb-space-4);border-radius:var(--cb-radius-full);background:var(--cb-color-surface-950);color:#fff;box-shadow:var(--cb-shadow-md)}\n"], dependencies: [{ kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }, { kind: "component", type: CbSpinner, selector: "cb-spinner", inputs: ["size", "strokeWidth", "severity", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
2727
+ `, isInline: true, styles: [":host{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:var(--cb-pdf-viewer-height, 40rem);min-height:16rem;overflow:hidden;border:var(--cb-border-width, 1px) solid var(--cb-color-border);border-radius:var(--cb-radius-lg);background:var(--cb-color-surface);color:var(--cb-color-text);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm);container-type:inline-size}.cb-pdf__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--cb-space-2);padding:var(--cb-space-2) var(--cb-space-3);border-bottom:var(--cb-border-width, 1px) solid var(--cb-color-border);background:var(--cb-color-surface)}.cb-pdf__group{display:inline-flex;align-items:center;gap:var(--cb-space-1)}.cb-pdf__spacer{flex:1}.cb-pdf__tool{display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;padding:0;border:none;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text);cursor:pointer;transition:background-color var(--cb-duration-fast) var(--cb-ease)}.cb-pdf__tool:hover:not(:disabled){background:var(--cb-color-surface-hover)}.cb-pdf__tool[aria-expanded=true]{background:var(--cb-color-surface-hover);color:var(--cb-color-primary-text)}.cb-pdf__tool:disabled{opacity:var(--cb-opacity-disabled, .5);cursor:default}.cb-pdf__tool:focus-visible,.cb-pdf__field:focus-visible,.cb-pdf__thumb:focus-visible,.cb-pdf__submit:focus-visible,.cb-pdf__viewport:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-pdf__field{height:2.25rem;padding:0 var(--cb-space-2);border:var(--cb-border-width, 1px) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-md);background:var(--cb-color-surface);color:var(--cb-color-text);font:inherit}.cb-pdf__field:disabled{opacity:var(--cb-opacity-disabled, .5)}.cb-pdf__page-input{width:3.25rem;text-align:center}.cb-pdf__total{min-width:2.5rem;color:var(--cb-color-text-muted);white-space:nowrap}.cb-pdf__zoom{width:9.5rem}.cb-pdf__search{position:relative}.cb-pdf__search-icon{position:absolute;left:.625rem;color:var(--cb-color-text-muted);pointer-events:none}.cb-pdf__search-input{width:14rem;padding-left:2rem}.cb-pdf__status{min-width:4.5rem;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs);text-align:center;white-space:nowrap}@container (max-width: 40rem){.cb-pdf__zoom,.cb-pdf__status{display:none}.cb-pdf__search-input{width:9rem}}.cb-pdf__body{display:flex;flex:1;min-height:0}.cb-pdf__sidebar{display:flex;flex-direction:column;gap:var(--cb-space-3);flex:none;width:10rem;padding:var(--cb-space-3) 0;overflow-y:auto;border-right:var(--cb-border-width, 1px) solid var(--cb-color-border);background:var(--cb-color-surface)}.cb-pdf__sidebar--tabs{width:12rem}.cb-pdf__sidebar--outline{width:16rem}.cb-pdf__thumbs{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-3)}.cb-pdf__views{display:flex;gap:2px;margin:0 var(--cb-space-2);padding:2px;border-radius:var(--cb-radius-md);background:var(--cb-color-surface-hover)}.cb-pdf__view{flex:1;min-width:0;padding:.25rem .375rem;border:0;border-radius:calc(var(--cb-radius-md) - 2px);background:transparent;color:var(--cb-color-text-muted);font:inherit;font-size:var(--cb-text-xs);font-weight:var(--cb-font-weight-medium);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer}.cb-pdf__view[aria-pressed=true]{background:var(--cb-color-surface-raised);color:var(--cb-color-text-strong);box-shadow:0 1px 2px #0f172a1f}.cb-pdf__view:focus-visible,.cb-pdf__outline-link:focus-visible,.cb-pdf__outline-toggle:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}.cb-pdf__outline{padding:0 var(--cb-space-2)}.cb-pdf__outline-list{margin:0;padding:0;list-style:none}.cb-pdf__outline-list .cb-pdf__outline-list{padding-left:1rem}.cb-pdf__outline-item{display:flex;flex-wrap:wrap;align-items:flex-start}.cb-pdf__outline-item>.cb-pdf__outline-list{flex-basis:100%}.cb-pdf__outline-toggle{display:inline-flex;flex:none;align-items:center;justify-content:center;width:1.5rem;height:1.75rem;padding:0;border:0;border-radius:var(--cb-radius-sm);background:transparent;color:var(--cb-color-text-muted);cursor:pointer}.cb-pdf__outline-link{flex:1;min-width:0;padding:.3rem .375rem;border:0;border-radius:var(--cb-radius-sm);background:transparent;color:var(--cb-color-text);font:inherit;font-size:var(--cb-text-sm);line-height:1.3;text-align:start;cursor:pointer}.cb-pdf__outline-item>.cb-pdf__outline-link:first-child{margin-left:1.5rem}.cb-pdf__outline-link:hover,.cb-pdf__outline-toggle:hover{background:var(--cb-color-surface-hover)}.cb-pdf__outline-link--bold{font-weight:var(--cb-font-weight-semibold)}.cb-pdf__outline-link--italic{font-style:italic}.cb-pdf__thumb{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-1);padding:var(--cb-space-1);border:2px solid transparent;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text-muted);font:inherit;font-size:var(--cb-text-xs);cursor:pointer}.cb-pdf__thumb:hover{background:var(--cb-color-surface-hover)}.cb-pdf__thumb.cb-is-active{border-color:var(--cb-color-primary-500);color:var(--cb-color-text-strong);font-weight:var(--cb-font-weight-semibold)}.cb-pdf__thumb-canvas{display:block;background:#fff;box-shadow:0 1px 3px #0f172a33}:host ::ng-deep .cb-pdf__thumb-canvas canvas{display:block;width:100%;height:100%}.cb-pdf__viewport{position:relative;flex:1;min-width:0;overflow:auto;background:var(--cb-color-surface-subtle)}.cb-pdf__pages{display:flex;flex-direction:column;align-items:center;gap:16px;padding:16px;width:max-content;min-width:100%;box-sizing:border-box}.cb-pdf__page{--user-unit: 1;--total-scale-factor: calc(var(--scale-factor) * var(--user-unit));--scale-round-x: 1px;--scale-round-y: 1px;position:relative;flex:none;background:#fff;box-shadow:0 1px 3px #0f172a2e,0 4px 12px #0f172a14;direction:ltr}.cb-pdf__canvas,:host ::ng-deep .cb-pdf__canvas canvas{position:absolute;inset:0;width:100%;height:100%}.cb-pdf__text{position:absolute;inset:0;overflow:clip;opacity:1;line-height:1;text-align:initial;letter-spacing:normal;word-spacing:normal;-webkit-text-size-adjust:none;text-size-adjust:none;forced-color-adjust:none;transform-origin:0 0;caret-color:CanvasText;z-index:0;--min-font-size: 1;--text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size));--min-font-size-inv: calc(1 / var(--min-font-size))}.cb-pdf__text[data-main-rotation=\"90\"]{transform:rotate(90deg) translateY(-100%)}.cb-pdf__text[data-main-rotation=\"180\"]{transform:rotate(180deg) translate(-100%,-100%)}.cb-pdf__text[data-main-rotation=\"270\"]{transform:rotate(270deg) translate(-100%)}.cb-pdf__annotations{position:absolute;inset:0;z-index:1;transform-origin:0 0;pointer-events:none}.cb-pdf__annotations[data-main-rotation=\"90\"]{transform:rotate(90deg) translateY(-100%)}.cb-pdf__annotations[data-main-rotation=\"180\"]{transform:rotate(180deg) translate(-100%,-100%)}.cb-pdf__annotations[data-main-rotation=\"270\"]{transform:rotate(270deg) translate(-100%)}:host ::ng-deep .cb-pdf__annotations section{position:absolute;box-sizing:border-box;text-align:initial;transform-origin:0 0;pointer-events:auto}:host ::ng-deep .cb-pdf__annotations [hidden]{display:none!important}:host ::ng-deep .cb-pdf__annotations .linkAnnotation>a{position:absolute;inset:0;border-radius:2px}:host ::ng-deep .cb-pdf__annotations .linkAnnotation>a:hover{background:#0e74901f}:host ::ng-deep .cb-pdf__annotations .linkAnnotation>a:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}:host ::ng-deep .cb-pdf__annotations :is(.textWidgetAnnotation,.choiceWidgetAnnotation) :is(input,textarea,select){width:100%;height:100%;box-sizing:border-box;margin:0;padding:0 calc(2px * var(--total-scale-factor));border:1px solid transparent;border-radius:0;background:#2563eb1a;color:#111827;font-family:Helvetica,Arial,sans-serif;font-size:calc(9px * var(--total-scale-factor));vertical-align:top}:host ::ng-deep .cb-pdf__annotations .textWidgetAnnotation textarea{padding-top:calc(2px * var(--total-scale-factor));resize:none}:host ::ng-deep .cb-pdf__annotations :is(.textWidgetAnnotation,.choiceWidgetAnnotation) :is(input,textarea,select):hover{border-color:#2563eb99}:host ::ng-deep .cb-pdf__annotations :is(input,textarea,select):focus{border-color:transparent;outline:2px solid var(--cb-color-focus-ring);outline-offset:0;background:#2563eb0a}:host ::ng-deep .cb-pdf__annotations :is(input,textarea,select):disabled{background:transparent}:host ::ng-deep .cb-pdf__annotations .buttonWidgetAnnotation :is(.checkBox,.radioButton) input{width:100%;height:100%;margin:0;accent-color:var(--cb-color-primary-600, #0e7490);cursor:pointer}:host ::ng-deep .cb-pdf__annotations .buttonWidgetAnnotation.checkBox input,:host ::ng-deep .cb-pdf__annotations .buttonWidgetAnnotation.radioButton input{width:100%;height:100%;margin:0;accent-color:var(--cb-color-primary-600, #0e7490);cursor:pointer}:host ::ng-deep .cb-pdf__text :is(span,br){position:absolute;color:transparent;white-space:pre;cursor:text;transform-origin:0% 0%;-webkit-user-select:text;user-select:text}:host ::ng-deep .cb-pdf__text>:not(.markedContent),:host ::ng-deep .cb-pdf__text .markedContent span:not(.markedContent){--font-height: 0;--scale-x: 1;--rotate: 0deg;z-index:1;font-size:calc(var(--text-scale-factor) * var(--font-height));transform:rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv))}:host ::ng-deep .cb-pdf__text .markedContent{display:contents}:host ::ng-deep .cb-pdf__text span[role=img]{-webkit-user-select:none;user-select:none;cursor:default}:host ::ng-deep .cb-pdf__text ::selection{background:color-mix(in srgb,var(--cb-color-primary-500) 35%,transparent);color:transparent}:host ::ng-deep .cb-pdf__text .cb-pdf__hl{position:relative;margin:-1px;padding:1px;border-radius:3px;background:#facc1573;font-size:inherit;transform:none}:host ::ng-deep .cb-pdf__text .cb-pdf__hl.cb-is-selected{background:#f973168c;outline:2px solid rgb(234 88 12)}@media(forced-colors:active){:host ::ng-deep .cb-pdf__text .cb-pdf__hl{background:transparent;outline:1px solid Highlight}:host ::ng-deep .cb-pdf__text .cb-pdf__hl.cb-is-selected{outline:3px solid Highlight}}.cb-pdf__message{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--cb-space-3);height:100%;padding:var(--cb-space-6);color:var(--cb-color-text-muted);text-align:center}.cb-pdf__message--error{color:var(--cb-color-danger)}.cb-pdf__password{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-2);max-width:24rem;margin:var(--cb-space-6) auto;padding:var(--cb-space-6);border:var(--cb-border-width, 1px) solid var(--cb-color-border);border-radius:var(--cb-radius-lg);background:var(--cb-color-surface);text-align:center}.cb-pdf__password-text{margin:0}.cb-pdf__password-label{align-self:flex-start;font-weight:var(--cb-font-weight-medium)}.cb-pdf__password-row{display:flex;gap:var(--cb-space-2);width:100%}.cb-pdf__password-row .cb-pdf__field{flex:1;min-width:0}.cb-pdf__password-error{margin:0;color:var(--cb-color-danger)}.cb-pdf__submit{height:2.25rem;padding:0 var(--cb-space-4);border:none;border-radius:var(--cb-radius-md);background:var(--cb-color-primary-500);color:var(--cb-color-primary-contrast);font:inherit;font-weight:var(--cb-font-weight-semibold);cursor:pointer}.cb-pdf__submit:hover{background:var(--cb-color-primary-600)}.cb-pdf__toast{position:absolute;left:50%;bottom:var(--cb-space-4);transform:translate(-50%);padding:var(--cb-space-2) var(--cb-space-4);border-radius:var(--cb-radius-full);background:var(--cb-color-surface-950);color:#fff;box-shadow:var(--cb-shadow-md)}.cb-pdf__tool[aria-pressed=true]{background:color-mix(in srgb,var(--cb-color-primary-500) 16%,transparent);color:var(--cb-color-primary-700)}:host-context([data-theme=\"dark\"]) .cb-pdf__tool[aria-pressed=true]{color:var(--cb-color-primary-300)}.cb-pdf__color{position:relative;display:inline-flex;align-items:center}.cb-pdf__swatch{position:absolute;left:.625rem;width:.875rem;height:.875rem;border:2px solid;border-radius:var(--cb-radius-full);pointer-events:none}.cb-pdf__color-select{padding-left:1.875rem}.cb-pdf__hint{position:sticky;top:var(--cb-space-2);z-index:4;box-sizing:border-box;width:max-content;max-width:calc(100% - 2rem);height:2rem;margin:var(--cb-space-2) auto calc(-2rem - var(--cb-space-2));padding:0 var(--cb-space-3);overflow:hidden;border-radius:var(--cb-radius-full);background:var(--cb-color-surface-950, #0f172a);color:#fff;font-size:var(--cb-text-xs);line-height:2rem;text-overflow:ellipsis;white-space:nowrap;box-shadow:var(--cb-shadow-md)}.cb-pdf__marks{position:absolute;inset:0;z-index:2;pointer-events:none}.cb-pdf__marks--capture{pointer-events:auto;cursor:cell;touch-action:none}.cb-pdf__marks--ink{cursor:crosshair}.cb-pdf__mark{position:absolute;box-sizing:border-box;pointer-events:auto;border-radius:2px;outline-offset:2px;touch-action:none}.cb-pdf__mark:focus-visible,.cb-pdf__mark.cb-is-selected{outline:2px solid var(--cb-color-focus-ring)}.cb-pdf__marks--pass .cb-pdf__mark--highlight{pointer-events:none}.cb-pdf__mark--highlight{cursor:pointer}.cb-pdf__hl-rect{position:absolute;background:color-mix(in srgb,var(--cb-pdf-mark-light) 55%,transparent)}.cb-pdf__hl-badge{position:absolute;top:-.35rem;right:-.35rem;width:.625rem;height:.625rem;border:2px solid #fff;border-radius:var(--cb-radius-full);background:var(--cb-pdf-mark-dark)}.cb-pdf__mark--note,.cb-pdf__mark--signature,.cb-pdf__mark--ink{cursor:move}.cb-pdf__note-text{display:block;box-sizing:border-box;padding:.1em .3em;border:1px dashed color-mix(in srgb,var(--cb-pdf-mark-dark) 45%,transparent);border-radius:2px;background:transparent;color:var(--cb-pdf-mark-dark);font-family:Helvetica,Arial,sans-serif;line-height:1.2;white-space:pre}.cb-pdf__ink,.cb-pdf__stroke{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}.cb-pdf__ink path,.cb-pdf__stroke polyline{fill:none;stroke:var(--cb-pdf-mark-dark);stroke-linecap:round;stroke-linejoin:round}.cb-pdf__sig{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none;-webkit-user-drag:none}.cb-pdf__resize{position:absolute;right:-.4rem;bottom:-.4rem;width:.8rem;height:.8rem;border:2px solid #fff;border-radius:2px;background:var(--cb-color-focus-ring);cursor:nwse-resize}.cb-pdf__card{position:absolute;z-index:3;display:flex;flex-direction:column;gap:var(--cb-space-2);box-sizing:border-box;width:16rem;padding:var(--cb-space-3);border:var(--cb-border-width, 1px) solid var(--cb-color-border);border-radius:var(--cb-radius-md);background:var(--cb-color-surface-raised, var(--cb-color-surface));color:var(--cb-color-text);font-size:var(--cb-text-sm);box-shadow:var(--cb-shadow-lg);pointer-events:auto;cursor:auto}.cb-pdf__card-label{font-weight:var(--cb-font-weight-medium)}.cb-pdf__card-text{height:auto;min-height:4.5rem;padding:var(--cb-space-2);resize:vertical}.cb-pdf__card-actions,.cb-pdf__sign-actions{display:flex;justify-content:flex-end;gap:var(--cb-space-2)}.cb-pdf__card-btn{height:2rem;padding:0 var(--cb-space-3);border:var(--cb-border-width, 1px) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text);font:inherit;cursor:pointer}.cb-pdf__card-btn:hover:not(:disabled){background:var(--cb-color-surface-hover)}.cb-pdf__card-btn--primary{border-color:var(--cb-color-primary-500);background:var(--cb-color-primary-500);color:var(--cb-color-primary-contrast);font-weight:var(--cb-font-weight-semibold)}.cb-pdf__card-btn--primary:hover:not(:disabled){background:var(--cb-color-primary-600)}.cb-pdf__card-btn:disabled{opacity:var(--cb-opacity-disabled, .5);cursor:default}.cb-pdf__card-btn:focus-visible,.cb-pdf__card-text:focus-visible,.cb-pdf__notes-link:focus-visible,.cb-pdf__notes-remove:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-pdf__notes-panel{padding:0 var(--cb-space-2)}.cb-pdf__notes-empty{margin:0;padding:var(--cb-space-2);color:var(--cb-color-text-muted)}.cb-pdf__notes-list{display:flex;flex-direction:column;gap:2px;margin:0;padding:0;list-style:none}.cb-pdf__notes-item{display:flex;align-items:flex-start;gap:2px}.cb-pdf__notes-link{display:flex;flex:1;align-items:flex-start;gap:var(--cb-space-2);min-width:0;padding:var(--cb-space-2);border:0;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text);font:inherit;text-align:left;cursor:pointer}.cb-pdf__notes-link:hover,.cb-pdf__notes-remove:hover{background:var(--cb-color-surface-hover)}.cb-pdf__notes-swatch{flex:none;width:.625rem;height:.625rem;margin-top:.3rem;border-radius:var(--cb-radius-full)}.cb-pdf__notes-text{display:flex;flex-direction:column;min-width:0}.cb-pdf__notes-page{color:var(--cb-color-text-muted);font-size:var(--cb-text-xs)}.cb-pdf__notes-label{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow-wrap:anywhere}.cb-pdf__notes-remove{display:inline-flex;flex:none;align-items:center;justify-content:center;width:2rem;height:2rem;border:0;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text-muted);cursor:pointer}.cb-pdf__sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}@media(forced-colors:active){.cb-pdf__hl-rect{background:transparent;outline:2px solid Mark}.cb-pdf__note-text{border-color:CanvasText}}\n"], dependencies: [{ kind: "component", type: CbDialog, selector: "cb-dialog", inputs: ["visible", "header", "closable", "dismissableMask", "closeOnEscape", "width", "ariaLabel"], outputs: ["visibleChange", "onHide", "onShow"] }, { kind: "component", type: CbIcon, selector: "cb-icon", inputs: ["name", "size", "spin", "ariaLabel"] }, { kind: "component", type: CbSignaturePad, selector: "cb-signature-pad", inputs: ["value", "format", "mode", "typing", "penColor", "background", "minWidth", "maxWidth", "height", "font", "placeholder", "disabled", "readonly", "invalid", "ariaLabel"], outputs: ["valueChange", "modeChange", "signStart", "signEnd", "cleared"] }, { kind: "component", type: CbSpinner, selector: "cb-spinner", inputs: ["size", "strokeWidth", "severity", "ariaLabel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
1273
2728
  }
1274
2729
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbPdfViewer, decorators: [{
1275
2730
  type: Component,
1276
- args: [{ selector: 'cb-pdf-viewer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbIcon, CbSpinner], host: {
2731
+ args: [{ selector: 'cb-pdf-viewer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbDialog, CbIcon, CbSignaturePad, CbSpinner, NgTemplateOutlet], host: {
1277
2732
  class: 'cb-pdf-viewer',
1278
2733
  role: 'region',
1279
2734
  '[attr.aria-label]': 'ariaLabel() || t().pdfViewer',
@@ -1361,6 +2816,70 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1361
2816
  }
1362
2817
  </div>
1363
2818
 
2819
+ @if (annotatable()) {
2820
+ <div class="cb-pdf__group" role="group" [attr.aria-label]="t().pdfAnnotate">
2821
+ <!-- mousedown sin default: pulsar la herramienta no quita la selección de texto. -->
2822
+ <button
2823
+ type="button"
2824
+ class="cb-pdf__tool"
2825
+ [attr.aria-label]="t().pdfHighlight"
2826
+ [attr.aria-pressed]="tool() === 'highlight'"
2827
+ [attr.title]="t().pdfHighlight"
2828
+ [disabled]="state() !== 'ready'"
2829
+ (mousedown)="$event.preventDefault()"
2830
+ (click)="onHighlightTool()"
2831
+ >
2832
+ <cb-icon name="highlighter" [size]="18" aria-hidden="true" />
2833
+ </button>
2834
+ <button
2835
+ type="button"
2836
+ class="cb-pdf__tool"
2837
+ [attr.aria-label]="t().pdfNote"
2838
+ [attr.aria-pressed]="tool() === 'note'"
2839
+ [attr.title]="t().pdfNote"
2840
+ [disabled]="state() !== 'ready'"
2841
+ (click)="onNoteTool($event)"
2842
+ >
2843
+ <cb-icon name="sticky-note" [size]="18" aria-hidden="true" />
2844
+ </button>
2845
+ <button
2846
+ type="button"
2847
+ class="cb-pdf__tool"
2848
+ [attr.aria-label]="t().pdfDraw"
2849
+ [attr.aria-pressed]="tool() === 'ink'"
2850
+ [attr.title]="t().pdfDraw"
2851
+ [disabled]="state() !== 'ready'"
2852
+ (click)="toggleTool('ink')"
2853
+ >
2854
+ <cb-icon name="pen-line" [size]="18" aria-hidden="true" />
2855
+ </button>
2856
+ <button
2857
+ type="button"
2858
+ class="cb-pdf__tool"
2859
+ aria-haspopup="dialog"
2860
+ [attr.aria-label]="t().pdfSign"
2861
+ [attr.title]="t().pdfSign"
2862
+ [disabled]="state() !== 'ready'"
2863
+ (click)="openSignature()"
2864
+ >
2865
+ <cb-icon name="signature" [size]="18" aria-hidden="true" />
2866
+ </button>
2867
+ <span class="cb-pdf__color">
2868
+ <span class="cb-pdf__swatch" aria-hidden="true" [style.background]="colors[annotationColor()].light" [style.border-color]="colors[annotationColor()].dark"></span>
2869
+ <select
2870
+ class="cb-pdf__field cb-pdf__color-select"
2871
+ [attr.aria-label]="t().pdfColor"
2872
+ [disabled]="state() !== 'ready'"
2873
+ (change)="annotationColor.set($any($event.target).value)"
2874
+ >
2875
+ @for (c of colorNames; track c) {
2876
+ <option [value]="c" [selected]="annotationColor() === c">{{ colorLabel(c) }}</option>
2877
+ }
2878
+ </select>
2879
+ </span>
2880
+ </div>
2881
+ }
2882
+
1364
2883
  <span class="cb-pdf__spacer"></span>
1365
2884
 
1366
2885
  @if (showSearch()) {
@@ -1391,7 +2910,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1391
2910
  @if (downloadable() || printable()) {
1392
2911
  <div class="cb-pdf__group">
1393
2912
  @if (downloadable()) {
1394
- <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().download" [disabled]="state() !== 'ready'" (click)="download()">
2913
+ <button
2914
+ type="button"
2915
+ class="cb-pdf__tool"
2916
+ [attr.aria-label]="annotations().length ? t().pdfDownloadAnnotated : formModified() ? t().pdfDownloadFilled : t().download"
2917
+ [attr.title]="annotations().length ? t().pdfDownloadAnnotated : formModified() ? t().pdfDownloadFilled : null"
2918
+ [disabled]="state() !== 'ready'"
2919
+ (click)="download()"
2920
+ >
1395
2921
  <cb-icon name="download" [size]="18" aria-hidden="true" />
1396
2922
  </button>
1397
2923
  }
@@ -1413,7 +2939,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1413
2939
 
1414
2940
  <div class="cb-pdf__body">
1415
2941
  @if (state() === 'ready' && sidebar() && showThumbnails()) {
1416
- <nav class="cb-pdf__sidebar" [id]="uid + '-thumbs'" [attr.aria-label]="t().thumbnails">
2942
+ <div
2943
+ class="cb-pdf__sidebar"
2944
+ [class.cb-pdf__sidebar--tabs]="sidebarTabs().length > 1"
2945
+ [class.cb-pdf__sidebar--outline]="activeView() !== 'thumbnails' || sidebarTabs().length > 2"
2946
+ [id]="uid + '-thumbs'"
2947
+ >
2948
+ @if (sidebarTabs().length > 1) {
2949
+ <div class="cb-pdf__views" role="group" [attr.aria-label]="t().pdfSidebarView">
2950
+ @for (view of sidebarTabs(); track view) {
2951
+ <button type="button" class="cb-pdf__view" [attr.aria-pressed]="activeView() === view" (click)="sidebarView.set(view)">
2952
+ {{ view === 'outline' ? t().pdfOutline : view === 'annotations' ? t().pdfAnnotations : t().thumbnails }}
2953
+ </button>
2954
+ }
2955
+ </div>
2956
+ }
2957
+ @if (activeView() === 'outline') {
2958
+ <nav class="cb-pdf__outline" [attr.aria-label]="t().pdfOutline">
2959
+ <ng-container *ngTemplateOutlet="outlineList; context: { $implicit: outline(), path: '' }" />
2960
+ </nav>
2961
+ } @else if (activeView() === 'annotations') {
2962
+ <section class="cb-pdf__notes-panel" [attr.aria-label]="t().pdfAnnotations">
2963
+ @if (!sortedAnnotations().length) {
2964
+ <p class="cb-pdf__notes-empty">{{ t().pdfNoAnnotations }}</p>
2965
+ } @else {
2966
+ <ul class="cb-pdf__notes-list">
2967
+ @for (a of sortedAnnotations(); track a.id) {
2968
+ <li class="cb-pdf__notes-item">
2969
+ <button type="button" class="cb-pdf__notes-link" (click)="revealAnnotation(a)">
2970
+ <span class="cb-pdf__notes-swatch" aria-hidden="true" [style.background]="swatchOf(a)"></span>
2971
+ <span class="cb-pdf__notes-text">
2972
+ <!-- La coma oculta separa la página del texto en el nombre del botón. -->
2973
+ <span class="cb-pdf__notes-page">{{ format(t().pageOf, a.page, numPages()) }}<span class="cb-pdf__sr">, </span></span>
2974
+ <span class="cb-pdf__notes-label">{{ labelOf(a) }}</span>
2975
+ </span>
2976
+ </button>
2977
+ <button type="button" class="cb-pdf__notes-remove" [attr.aria-label]="t().pdfDeleteAnnotation + ': ' + labelOf(a)" (click)="removeAnnotation(a)">
2978
+ <cb-icon name="trash" [size]="14" aria-hidden="true" />
2979
+ </button>
2980
+ </li>
2981
+ }
2982
+ </ul>
2983
+ }
2984
+ </section>
2985
+ } @else {
2986
+ <nav class="cb-pdf__thumbs" [attr.aria-label]="t().thumbnails">
1417
2987
  @for (thumb of thumbBoxes(); track $index; let i = $index) {
1418
2988
  <button
1419
2989
  #thumbEl
@@ -1430,7 +3000,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1430
3000
  <span class="cb-pdf__thumb-label" aria-hidden="true">{{ i + 1 }}</span>
1431
3001
  </button>
1432
3002
  }
1433
- </nav>
3003
+ </nav>
3004
+ }
3005
+ </div>
1434
3006
  }
1435
3007
 
1436
3008
  <div
@@ -1476,7 +3048,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1476
3048
  </form>
1477
3049
  }
1478
3050
  @case ('ready') {
1479
- <div class="cb-pdf__pages">
3051
+ @if (toolHint(); as hint) {
3052
+ <div class="cb-pdf__hint">{{ hint }}</div>
3053
+ }
3054
+ <div class="cb-pdf__pages" (pointerup)="onPagesPointerUp()">
1480
3055
  @for (box of pageBoxes(); track $index; let i = $index) {
1481
3056
  <div
1482
3057
  #pageEl
@@ -1491,6 +3066,97 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1491
3066
  >
1492
3067
  <div class="cb-pdf__canvas"></div>
1493
3068
  <div class="cb-pdf__text"></div>
3069
+ <div class="cb-pdf__annotations" (input)="onFormInput()" (change)="onFormInput()"></div>
3070
+ @if (annotatable() || marks()[i]?.length) {
3071
+ <div
3072
+ class="cb-pdf__marks"
3073
+ [class.cb-pdf__marks--capture]="annotatable() && (tool() === 'ink' || tool() === 'note')"
3074
+ [class.cb-pdf__marks--ink]="tool() === 'ink'"
3075
+ [class.cb-pdf__marks--pass]="tool() === 'highlight'"
3076
+ (pointerdown)="onMarksPointerDown($event, i + 1)"
3077
+ >
3078
+ @for (m of marks()[i]; track m.a.id) {
3079
+ <div
3080
+ class="cb-pdf__mark"
3081
+ [class]="'cb-pdf__mark cb-pdf__mark--' + m.a.type"
3082
+ [class.cb-is-selected]="selectedId() === m.a.id"
3083
+ [attr.data-id]="m.a.id"
3084
+ [attr.tabindex]="annotatable() ? 0 : null"
3085
+ [attr.role]="annotatable() ? 'button' : 'img'"
3086
+ [attr.aria-label]="m.label"
3087
+ [attr.aria-describedby]="annotatable() ? uid + '-mark-hint' : null"
3088
+ [attr.aria-expanded]="annotatable() && (m.a.type === 'note' || m.a.type === 'highlight') ? editingId() === m.a.id : null"
3089
+ [style.left.px]="m.box.left"
3090
+ [style.top.px]="m.box.top"
3091
+ [style.width.px]="m.a.type === 'note' ? null : m.box.width"
3092
+ [style.height.px]="m.a.type === 'note' ? null : m.box.height"
3093
+ [style.--cb-pdf-mark-light]="m.light"
3094
+ [style.--cb-pdf-mark-dark]="m.dark"
3095
+ (pointerdown)="onMarkPointerDown($event, m.a)"
3096
+ (keydown)="onMarkKeydown($event, m.a)"
3097
+ (focus)="select(m.a.id)"
3098
+ >
3099
+ @switch (m.a.type) {
3100
+ @case ('highlight') {
3101
+ @for (r of m.rects; track $index) {
3102
+ <span class="cb-pdf__hl-rect" [style.left.px]="r.left" [style.top.px]="r.top" [style.width.px]="r.width" [style.height.px]="r.height"></span>
3103
+ }
3104
+ @if (m.a.comment) {
3105
+ <span class="cb-pdf__hl-badge" aria-hidden="true"></span>
3106
+ }
3107
+ }
3108
+ @case ('note') {
3109
+ <span class="cb-pdf__note-text" [style.font-size.px]="m.fontPx" [style.min-width.px]="m.box.width">{{ m.a.text || ' ' }}</span>
3110
+ }
3111
+ @case ('ink') {
3112
+ <svg class="cb-pdf__ink" [attr.viewBox]="'0 0 ' + m.box.width + ' ' + m.box.height" aria-hidden="true">
3113
+ <path [attr.d]="m.d" [attr.stroke-width]="m.strokePx" />
3114
+ </svg>
3115
+ }
3116
+ @case ('signature') {
3117
+ <img class="cb-pdf__sig" [src]="m.a.image" alt="" draggable="false" />
3118
+ @if (annotatable() && selectedId() === m.a.id) {
3119
+ <span class="cb-pdf__resize" aria-hidden="true" (pointerdown)="onResizePointerDown($event, m.a)"></span>
3120
+ }
3121
+ }
3122
+ }
3123
+ </div>
3124
+ }
3125
+ @if (stroke(); as s) {
3126
+ @if (s.page === i + 1) {
3127
+ <svg class="cb-pdf__stroke" aria-hidden="true" [style.--cb-pdf-mark-dark]="colors[annotationColor()].dark">
3128
+ <polyline [attr.points]="s.points.join(' ')" [attr.stroke-width]="2 * box.scaleFactor" />
3129
+ </svg>
3130
+ }
3131
+ }
3132
+ @if (editingMark(); as e) {
3133
+ @if (e.a.page === i + 1) {
3134
+ <div
3135
+ class="cb-pdf__card"
3136
+ role="group"
3137
+ [attr.aria-label]="e.label"
3138
+ [style.left.px]="cardLeft(e, box.width)"
3139
+ [style.top.px]="e.box.top + (e.a.type === 'note' ? noteHeight(e) : e.box.height) + 6"
3140
+ (pointerdown)="$event.stopPropagation()"
3141
+ (keydown)="onCardKeydown($event)"
3142
+ >
3143
+ <label class="cb-pdf__card-label" [for]="uid + '-card'">{{ e.a.type === 'note' ? t().pdfNoteText : t().pdfComment }}</label>
3144
+ <textarea
3145
+ #cardText
3146
+ class="cb-pdf__field cb-pdf__card-text"
3147
+ rows="3"
3148
+ [id]="uid + '-card'"
3149
+ (input)="onCardInput($any($event.target).value)"
3150
+ ></textarea>
3151
+ <div class="cb-pdf__card-actions">
3152
+ <button type="button" class="cb-pdf__card-btn" (click)="removeAnnotation(e.a)">{{ t().pdfDeleteAnnotation }}</button>
3153
+ <button type="button" class="cb-pdf__card-btn cb-pdf__card-btn--primary" (click)="closeEditor(true)">{{ t().pdfDone }}</button>
3154
+ </div>
3155
+ </div>
3156
+ }
3157
+ }
3158
+ </div>
3159
+ }
1494
3160
  </div>
1495
3161
  }
1496
3162
  </div>
@@ -1499,15 +3165,62 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1499
3165
  </div>
1500
3166
  </div>
1501
3167
 
3168
+ @if (annotatable()) {
3169
+ <span class="cb-pdf__sr" [id]="uid + '-mark-hint'">{{ t().pdfAnnotationHint }}</span>
3170
+ <span class="cb-pdf__sr" aria-live="polite">{{ announcement() }}</span>
3171
+ <cb-dialog [(visible)]="signing" [header]="t().pdfSignTitle" width="34rem" (onHide)="onSignatureHide()">
3172
+ <cb-signature-pad [(value)]="signatureValue" background="transparent" [ariaLabel]="t().pdfSignTitle" />
3173
+ <div cb-dialog-footer class="cb-pdf__sign-actions">
3174
+ <button type="button" class="cb-pdf__card-btn" (click)="signing.set(false)">{{ t().pdfCancel }}</button>
3175
+ <button type="button" class="cb-pdf__card-btn cb-pdf__card-btn--primary" [disabled]="!signatureValue()" (click)="insertSignature()">
3176
+ {{ t().pdfInsertSignature }}
3177
+ </button>
3178
+ </div>
3179
+ </cb-dialog>
3180
+ }
3181
+
3182
+ <ng-template #outlineList let-items let-path="path">
3183
+ <ul class="cb-pdf__outline-list" [attr.role]="path ? 'group' : null">
3184
+ @for (item of items; track $index; let i = $index) {
3185
+ <li class="cb-pdf__outline-item">
3186
+ @if (item.items.length) {
3187
+ <button
3188
+ type="button"
3189
+ class="cb-pdf__outline-toggle"
3190
+ [attr.aria-expanded]="!isCollapsed(path + i)"
3191
+ [attr.aria-label]="format(isCollapsed(path + i) ? t().pdfExpandItem : t().pdfCollapseItem, item.title)"
3192
+ (click)="toggleOutline(path + i)"
3193
+ >
3194
+ <cb-icon [name]="isCollapsed(path + i) ? 'chevron-right' : 'chevron-down'" [size]="14" aria-hidden="true" />
3195
+ </button>
3196
+ }
3197
+ <button
3198
+ type="button"
3199
+ class="cb-pdf__outline-link"
3200
+ [class.cb-pdf__outline-link--bold]="item.bold"
3201
+ [class.cb-pdf__outline-link--italic]="item.italic"
3202
+ (click)="openOutlineItem(item)"
3203
+ >
3204
+ {{ item.title }}
3205
+ </button>
3206
+ @if (item.items.length && !isCollapsed(path + i)) {
3207
+ <ng-container *ngTemplateOutlet="outlineList; context: { $implicit: item.items, path: path + i + '.' }" />
3208
+ }
3209
+ </li>
3210
+ }
3211
+ </ul>
3212
+ </ng-template>
3213
+
1502
3214
  @if (printProgress(); as p) {
1503
3215
  <div class="cb-pdf__toast" role="status">{{ format(t().preparingPrint, p.done, p.total) }}</div>
1504
3216
  }
1505
- `, styles: [":host{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:var(--cb-pdf-viewer-height, 40rem);min-height:16rem;overflow:hidden;border:var(--cb-border-width, 1px) solid var(--cb-color-border);border-radius:var(--cb-radius-lg);background:var(--cb-color-surface);color:var(--cb-color-text);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm);container-type:inline-size}.cb-pdf__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--cb-space-2);padding:var(--cb-space-2) var(--cb-space-3);border-bottom:var(--cb-border-width, 1px) solid var(--cb-color-border);background:var(--cb-color-surface)}.cb-pdf__group{display:inline-flex;align-items:center;gap:var(--cb-space-1)}.cb-pdf__spacer{flex:1}.cb-pdf__tool{display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;padding:0;border:none;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text);cursor:pointer;transition:background-color var(--cb-duration-fast) var(--cb-ease)}.cb-pdf__tool:hover:not(:disabled){background:var(--cb-color-surface-hover)}.cb-pdf__tool[aria-expanded=true]{background:var(--cb-color-surface-hover);color:var(--cb-color-primary-600)}.cb-pdf__tool:disabled{opacity:var(--cb-opacity-disabled, .5);cursor:default}.cb-pdf__tool:focus-visible,.cb-pdf__field:focus-visible,.cb-pdf__thumb:focus-visible,.cb-pdf__submit:focus-visible,.cb-pdf__viewport:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-pdf__field{height:2.25rem;padding:0 var(--cb-space-2);border:var(--cb-border-width, 1px) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-md);background:var(--cb-color-surface);color:var(--cb-color-text);font:inherit}.cb-pdf__field:disabled{opacity:var(--cb-opacity-disabled, .5)}.cb-pdf__page-input{width:3.25rem;text-align:center}.cb-pdf__total{min-width:2.5rem;color:var(--cb-color-text-muted);white-space:nowrap}.cb-pdf__zoom{width:9.5rem}.cb-pdf__search{position:relative}.cb-pdf__search-icon{position:absolute;left:.625rem;color:var(--cb-color-text-muted);pointer-events:none}.cb-pdf__search-input{width:14rem;padding-left:2rem}.cb-pdf__status{min-width:4.5rem;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs);text-align:center;white-space:nowrap}@container (max-width: 40rem){.cb-pdf__zoom,.cb-pdf__status{display:none}.cb-pdf__search-input{width:9rem}}.cb-pdf__body{display:flex;flex:1;min-height:0}.cb-pdf__sidebar{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-3);flex:none;width:10rem;padding:var(--cb-space-3) 0;overflow-y:auto;border-right:var(--cb-border-width, 1px) solid var(--cb-color-border);background:var(--cb-color-surface)}.cb-pdf__thumb{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-1);padding:var(--cb-space-1);border:2px solid transparent;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text-muted);font:inherit;font-size:var(--cb-text-xs);cursor:pointer}.cb-pdf__thumb:hover{background:var(--cb-color-surface-hover)}.cb-pdf__thumb.cb-is-active{border-color:var(--cb-color-primary-500);color:var(--cb-color-text-strong);font-weight:var(--cb-font-weight-semibold)}.cb-pdf__thumb-canvas{display:block;background:#fff;box-shadow:0 1px 3px #0f172a33}:host ::ng-deep .cb-pdf__thumb-canvas canvas{display:block;width:100%;height:100%}.cb-pdf__viewport{position:relative;flex:1;min-width:0;overflow:auto;background:var(--cb-color-surface-subtle)}.cb-pdf__pages{display:flex;flex-direction:column;align-items:center;gap:16px;padding:16px;width:max-content;min-width:100%;box-sizing:border-box}.cb-pdf__page{--user-unit: 1;--total-scale-factor: calc(var(--scale-factor) * var(--user-unit));--scale-round-x: 1px;--scale-round-y: 1px;position:relative;flex:none;background:#fff;box-shadow:0 1px 3px #0f172a2e,0 4px 12px #0f172a14;direction:ltr}.cb-pdf__canvas,:host ::ng-deep .cb-pdf__canvas canvas{position:absolute;inset:0;width:100%;height:100%}.cb-pdf__text{position:absolute;inset:0;overflow:clip;opacity:1;line-height:1;text-align:initial;letter-spacing:normal;word-spacing:normal;-webkit-text-size-adjust:none;text-size-adjust:none;forced-color-adjust:none;transform-origin:0 0;caret-color:CanvasText;z-index:0;--min-font-size: 1;--text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size));--min-font-size-inv: calc(1 / var(--min-font-size))}.cb-pdf__text[data-main-rotation=\"90\"]{transform:rotate(90deg) translateY(-100%)}.cb-pdf__text[data-main-rotation=\"180\"]{transform:rotate(180deg) translate(-100%,-100%)}.cb-pdf__text[data-main-rotation=\"270\"]{transform:rotate(270deg) translate(-100%)}:host ::ng-deep .cb-pdf__text :is(span,br){position:absolute;color:transparent;white-space:pre;cursor:text;transform-origin:0% 0%;-webkit-user-select:text;user-select:text}:host ::ng-deep .cb-pdf__text>:not(.markedContent),:host ::ng-deep .cb-pdf__text .markedContent span:not(.markedContent){--font-height: 0;--scale-x: 1;--rotate: 0deg;z-index:1;font-size:calc(var(--text-scale-factor) * var(--font-height));transform:rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv))}:host ::ng-deep .cb-pdf__text .markedContent{display:contents}:host ::ng-deep .cb-pdf__text span[role=img]{-webkit-user-select:none;user-select:none;cursor:default}:host ::ng-deep .cb-pdf__text ::selection{background:color-mix(in srgb,var(--cb-color-primary-500) 35%,transparent);color:transparent}:host ::ng-deep .cb-pdf__text .cb-pdf__hl{position:relative;margin:-1px;padding:1px;border-radius:3px;background:#facc1573;font-size:inherit;transform:none}:host ::ng-deep .cb-pdf__text .cb-pdf__hl.cb-is-selected{background:#f973168c;outline:2px solid rgb(234 88 12)}@media(forced-colors:active){:host ::ng-deep .cb-pdf__text .cb-pdf__hl{background:transparent;outline:1px solid Highlight}:host ::ng-deep .cb-pdf__text .cb-pdf__hl.cb-is-selected{outline:3px solid Highlight}}.cb-pdf__message{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--cb-space-3);height:100%;padding:var(--cb-space-6);color:var(--cb-color-text-muted);text-align:center}.cb-pdf__message--error{color:var(--cb-color-danger)}.cb-pdf__password{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-2);max-width:24rem;margin:var(--cb-space-6) auto;padding:var(--cb-space-6);border:var(--cb-border-width, 1px) solid var(--cb-color-border);border-radius:var(--cb-radius-lg);background:var(--cb-color-surface);text-align:center}.cb-pdf__password-text{margin:0}.cb-pdf__password-label{align-self:flex-start;font-weight:var(--cb-font-weight-medium)}.cb-pdf__password-row{display:flex;gap:var(--cb-space-2);width:100%}.cb-pdf__password-row .cb-pdf__field{flex:1;min-width:0}.cb-pdf__password-error{margin:0;color:var(--cb-color-danger)}.cb-pdf__submit{height:2.25rem;padding:0 var(--cb-space-4);border:none;border-radius:var(--cb-radius-md);background:var(--cb-color-primary-500);color:var(--cb-color-primary-contrast);font:inherit;font-weight:var(--cb-font-weight-semibold);cursor:pointer}.cb-pdf__submit:hover{background:var(--cb-color-primary-600)}.cb-pdf__toast{position:absolute;left:50%;bottom:var(--cb-space-4);transform:translate(-50%);padding:var(--cb-space-2) var(--cb-space-4);border-radius:var(--cb-radius-full);background:var(--cb-color-surface-950);color:#fff;box-shadow:var(--cb-shadow-md)}\n"] }]
1506
- }], ctorParameters: () => [], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], password: [{ type: i0.Input, args: [{ isSignal: true, alias: "password", required: false }] }], withCredentials: [{ type: i0.Input, args: [{ isSignal: true, alias: "withCredentials", required: false }] }], httpHeaders: [{ type: i0.Input, args: [{ isSignal: true, alias: "httpHeaders", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }, { type: i0.Output, args: ["zoomChange"] }], rotation: [{ type: i0.Input, args: [{ isSignal: true, alias: "rotation", required: false }] }, { type: i0.Output, args: ["rotationChange"] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: false }] }, { type: i0.Output, args: ["sidebarChange"] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], showThumbnails: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThumbnails", required: false }] }], showRotate: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRotate", required: false }] }], downloadable: [{ type: i0.Input, args: [{ isSignal: true, alias: "downloadable", required: false }] }], printable: [{ type: i0.Input, args: [{ isSignal: true, alias: "printable", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], loadError: [{ type: i0.Output, args: ["loadError"] }], viewport: [{ type: i0.ViewChild, args: ['viewport', { isSignal: true }] }], pageEls: [{ type: i0.ViewChildren, args: ['pageEl', { isSignal: true }] }], thumbEls: [{ type: i0.ViewChildren, args: ['thumbEl', { isSignal: true }] }], searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }], passwordInput: [{ type: i0.ViewChild, args: ['passwordInput', { isSignal: true }] }] } });
3217
+ `, styles: [":host{position:relative;display:flex;flex-direction:column;box-sizing:border-box;width:100%;height:var(--cb-pdf-viewer-height, 40rem);min-height:16rem;overflow:hidden;border:var(--cb-border-width, 1px) solid var(--cb-color-border);border-radius:var(--cb-radius-lg);background:var(--cb-color-surface);color:var(--cb-color-text);font-family:var(--cb-font-sans);font-size:var(--cb-text-sm);container-type:inline-size}.cb-pdf__toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:var(--cb-space-2);padding:var(--cb-space-2) var(--cb-space-3);border-bottom:var(--cb-border-width, 1px) solid var(--cb-color-border);background:var(--cb-color-surface)}.cb-pdf__group{display:inline-flex;align-items:center;gap:var(--cb-space-1)}.cb-pdf__spacer{flex:1}.cb-pdf__tool{display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;padding:0;border:none;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text);cursor:pointer;transition:background-color var(--cb-duration-fast) var(--cb-ease)}.cb-pdf__tool:hover:not(:disabled){background:var(--cb-color-surface-hover)}.cb-pdf__tool[aria-expanded=true]{background:var(--cb-color-surface-hover);color:var(--cb-color-primary-text)}.cb-pdf__tool:disabled{opacity:var(--cb-opacity-disabled, .5);cursor:default}.cb-pdf__tool:focus-visible,.cb-pdf__field:focus-visible,.cb-pdf__thumb:focus-visible,.cb-pdf__submit:focus-visible,.cb-pdf__viewport:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-pdf__field{height:2.25rem;padding:0 var(--cb-space-2);border:var(--cb-border-width, 1px) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-md);background:var(--cb-color-surface);color:var(--cb-color-text);font:inherit}.cb-pdf__field:disabled{opacity:var(--cb-opacity-disabled, .5)}.cb-pdf__page-input{width:3.25rem;text-align:center}.cb-pdf__total{min-width:2.5rem;color:var(--cb-color-text-muted);white-space:nowrap}.cb-pdf__zoom{width:9.5rem}.cb-pdf__search{position:relative}.cb-pdf__search-icon{position:absolute;left:.625rem;color:var(--cb-color-text-muted);pointer-events:none}.cb-pdf__search-input{width:14rem;padding-left:2rem}.cb-pdf__status{min-width:4.5rem;color:var(--cb-color-text-muted);font-size:var(--cb-text-xs);text-align:center;white-space:nowrap}@container (max-width: 40rem){.cb-pdf__zoom,.cb-pdf__status{display:none}.cb-pdf__search-input{width:9rem}}.cb-pdf__body{display:flex;flex:1;min-height:0}.cb-pdf__sidebar{display:flex;flex-direction:column;gap:var(--cb-space-3);flex:none;width:10rem;padding:var(--cb-space-3) 0;overflow-y:auto;border-right:var(--cb-border-width, 1px) solid var(--cb-color-border);background:var(--cb-color-surface)}.cb-pdf__sidebar--tabs{width:12rem}.cb-pdf__sidebar--outline{width:16rem}.cb-pdf__thumbs{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-3)}.cb-pdf__views{display:flex;gap:2px;margin:0 var(--cb-space-2);padding:2px;border-radius:var(--cb-radius-md);background:var(--cb-color-surface-hover)}.cb-pdf__view{flex:1;min-width:0;padding:.25rem .375rem;border:0;border-radius:calc(var(--cb-radius-md) - 2px);background:transparent;color:var(--cb-color-text-muted);font:inherit;font-size:var(--cb-text-xs);font-weight:var(--cb-font-weight-medium);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer}.cb-pdf__view[aria-pressed=true]{background:var(--cb-color-surface-raised);color:var(--cb-color-text-strong);box-shadow:0 1px 2px #0f172a1f}.cb-pdf__view:focus-visible,.cb-pdf__outline-link:focus-visible,.cb-pdf__outline-toggle:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}.cb-pdf__outline{padding:0 var(--cb-space-2)}.cb-pdf__outline-list{margin:0;padding:0;list-style:none}.cb-pdf__outline-list .cb-pdf__outline-list{padding-left:1rem}.cb-pdf__outline-item{display:flex;flex-wrap:wrap;align-items:flex-start}.cb-pdf__outline-item>.cb-pdf__outline-list{flex-basis:100%}.cb-pdf__outline-toggle{display:inline-flex;flex:none;align-items:center;justify-content:center;width:1.5rem;height:1.75rem;padding:0;border:0;border-radius:var(--cb-radius-sm);background:transparent;color:var(--cb-color-text-muted);cursor:pointer}.cb-pdf__outline-link{flex:1;min-width:0;padding:.3rem .375rem;border:0;border-radius:var(--cb-radius-sm);background:transparent;color:var(--cb-color-text);font:inherit;font-size:var(--cb-text-sm);line-height:1.3;text-align:start;cursor:pointer}.cb-pdf__outline-item>.cb-pdf__outline-link:first-child{margin-left:1.5rem}.cb-pdf__outline-link:hover,.cb-pdf__outline-toggle:hover{background:var(--cb-color-surface-hover)}.cb-pdf__outline-link--bold{font-weight:var(--cb-font-weight-semibold)}.cb-pdf__outline-link--italic{font-style:italic}.cb-pdf__thumb{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-1);padding:var(--cb-space-1);border:2px solid transparent;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text-muted);font:inherit;font-size:var(--cb-text-xs);cursor:pointer}.cb-pdf__thumb:hover{background:var(--cb-color-surface-hover)}.cb-pdf__thumb.cb-is-active{border-color:var(--cb-color-primary-500);color:var(--cb-color-text-strong);font-weight:var(--cb-font-weight-semibold)}.cb-pdf__thumb-canvas{display:block;background:#fff;box-shadow:0 1px 3px #0f172a33}:host ::ng-deep .cb-pdf__thumb-canvas canvas{display:block;width:100%;height:100%}.cb-pdf__viewport{position:relative;flex:1;min-width:0;overflow:auto;background:var(--cb-color-surface-subtle)}.cb-pdf__pages{display:flex;flex-direction:column;align-items:center;gap:16px;padding:16px;width:max-content;min-width:100%;box-sizing:border-box}.cb-pdf__page{--user-unit: 1;--total-scale-factor: calc(var(--scale-factor) * var(--user-unit));--scale-round-x: 1px;--scale-round-y: 1px;position:relative;flex:none;background:#fff;box-shadow:0 1px 3px #0f172a2e,0 4px 12px #0f172a14;direction:ltr}.cb-pdf__canvas,:host ::ng-deep .cb-pdf__canvas canvas{position:absolute;inset:0;width:100%;height:100%}.cb-pdf__text{position:absolute;inset:0;overflow:clip;opacity:1;line-height:1;text-align:initial;letter-spacing:normal;word-spacing:normal;-webkit-text-size-adjust:none;text-size-adjust:none;forced-color-adjust:none;transform-origin:0 0;caret-color:CanvasText;z-index:0;--min-font-size: 1;--text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size));--min-font-size-inv: calc(1 / var(--min-font-size))}.cb-pdf__text[data-main-rotation=\"90\"]{transform:rotate(90deg) translateY(-100%)}.cb-pdf__text[data-main-rotation=\"180\"]{transform:rotate(180deg) translate(-100%,-100%)}.cb-pdf__text[data-main-rotation=\"270\"]{transform:rotate(270deg) translate(-100%)}.cb-pdf__annotations{position:absolute;inset:0;z-index:1;transform-origin:0 0;pointer-events:none}.cb-pdf__annotations[data-main-rotation=\"90\"]{transform:rotate(90deg) translateY(-100%)}.cb-pdf__annotations[data-main-rotation=\"180\"]{transform:rotate(180deg) translate(-100%,-100%)}.cb-pdf__annotations[data-main-rotation=\"270\"]{transform:rotate(270deg) translate(-100%)}:host ::ng-deep .cb-pdf__annotations section{position:absolute;box-sizing:border-box;text-align:initial;transform-origin:0 0;pointer-events:auto}:host ::ng-deep .cb-pdf__annotations [hidden]{display:none!important}:host ::ng-deep .cb-pdf__annotations .linkAnnotation>a{position:absolute;inset:0;border-radius:2px}:host ::ng-deep .cb-pdf__annotations .linkAnnotation>a:hover{background:#0e74901f}:host ::ng-deep .cb-pdf__annotations .linkAnnotation>a:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:1px}:host ::ng-deep .cb-pdf__annotations :is(.textWidgetAnnotation,.choiceWidgetAnnotation) :is(input,textarea,select){width:100%;height:100%;box-sizing:border-box;margin:0;padding:0 calc(2px * var(--total-scale-factor));border:1px solid transparent;border-radius:0;background:#2563eb1a;color:#111827;font-family:Helvetica,Arial,sans-serif;font-size:calc(9px * var(--total-scale-factor));vertical-align:top}:host ::ng-deep .cb-pdf__annotations .textWidgetAnnotation textarea{padding-top:calc(2px * var(--total-scale-factor));resize:none}:host ::ng-deep .cb-pdf__annotations :is(.textWidgetAnnotation,.choiceWidgetAnnotation) :is(input,textarea,select):hover{border-color:#2563eb99}:host ::ng-deep .cb-pdf__annotations :is(input,textarea,select):focus{border-color:transparent;outline:2px solid var(--cb-color-focus-ring);outline-offset:0;background:#2563eb0a}:host ::ng-deep .cb-pdf__annotations :is(input,textarea,select):disabled{background:transparent}:host ::ng-deep .cb-pdf__annotations .buttonWidgetAnnotation :is(.checkBox,.radioButton) input{width:100%;height:100%;margin:0;accent-color:var(--cb-color-primary-600, #0e7490);cursor:pointer}:host ::ng-deep .cb-pdf__annotations .buttonWidgetAnnotation.checkBox input,:host ::ng-deep .cb-pdf__annotations .buttonWidgetAnnotation.radioButton input{width:100%;height:100%;margin:0;accent-color:var(--cb-color-primary-600, #0e7490);cursor:pointer}:host ::ng-deep .cb-pdf__text :is(span,br){position:absolute;color:transparent;white-space:pre;cursor:text;transform-origin:0% 0%;-webkit-user-select:text;user-select:text}:host ::ng-deep .cb-pdf__text>:not(.markedContent),:host ::ng-deep .cb-pdf__text .markedContent span:not(.markedContent){--font-height: 0;--scale-x: 1;--rotate: 0deg;z-index:1;font-size:calc(var(--text-scale-factor) * var(--font-height));transform:rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv))}:host ::ng-deep .cb-pdf__text .markedContent{display:contents}:host ::ng-deep .cb-pdf__text span[role=img]{-webkit-user-select:none;user-select:none;cursor:default}:host ::ng-deep .cb-pdf__text ::selection{background:color-mix(in srgb,var(--cb-color-primary-500) 35%,transparent);color:transparent}:host ::ng-deep .cb-pdf__text .cb-pdf__hl{position:relative;margin:-1px;padding:1px;border-radius:3px;background:#facc1573;font-size:inherit;transform:none}:host ::ng-deep .cb-pdf__text .cb-pdf__hl.cb-is-selected{background:#f973168c;outline:2px solid rgb(234 88 12)}@media(forced-colors:active){:host ::ng-deep .cb-pdf__text .cb-pdf__hl{background:transparent;outline:1px solid Highlight}:host ::ng-deep .cb-pdf__text .cb-pdf__hl.cb-is-selected{outline:3px solid Highlight}}.cb-pdf__message{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--cb-space-3);height:100%;padding:var(--cb-space-6);color:var(--cb-color-text-muted);text-align:center}.cb-pdf__message--error{color:var(--cb-color-danger)}.cb-pdf__password{display:flex;flex-direction:column;align-items:center;gap:var(--cb-space-2);max-width:24rem;margin:var(--cb-space-6) auto;padding:var(--cb-space-6);border:var(--cb-border-width, 1px) solid var(--cb-color-border);border-radius:var(--cb-radius-lg);background:var(--cb-color-surface);text-align:center}.cb-pdf__password-text{margin:0}.cb-pdf__password-label{align-self:flex-start;font-weight:var(--cb-font-weight-medium)}.cb-pdf__password-row{display:flex;gap:var(--cb-space-2);width:100%}.cb-pdf__password-row .cb-pdf__field{flex:1;min-width:0}.cb-pdf__password-error{margin:0;color:var(--cb-color-danger)}.cb-pdf__submit{height:2.25rem;padding:0 var(--cb-space-4);border:none;border-radius:var(--cb-radius-md);background:var(--cb-color-primary-500);color:var(--cb-color-primary-contrast);font:inherit;font-weight:var(--cb-font-weight-semibold);cursor:pointer}.cb-pdf__submit:hover{background:var(--cb-color-primary-600)}.cb-pdf__toast{position:absolute;left:50%;bottom:var(--cb-space-4);transform:translate(-50%);padding:var(--cb-space-2) var(--cb-space-4);border-radius:var(--cb-radius-full);background:var(--cb-color-surface-950);color:#fff;box-shadow:var(--cb-shadow-md)}.cb-pdf__tool[aria-pressed=true]{background:color-mix(in srgb,var(--cb-color-primary-500) 16%,transparent);color:var(--cb-color-primary-700)}:host-context([data-theme=\"dark\"]) .cb-pdf__tool[aria-pressed=true]{color:var(--cb-color-primary-300)}.cb-pdf__color{position:relative;display:inline-flex;align-items:center}.cb-pdf__swatch{position:absolute;left:.625rem;width:.875rem;height:.875rem;border:2px solid;border-radius:var(--cb-radius-full);pointer-events:none}.cb-pdf__color-select{padding-left:1.875rem}.cb-pdf__hint{position:sticky;top:var(--cb-space-2);z-index:4;box-sizing:border-box;width:max-content;max-width:calc(100% - 2rem);height:2rem;margin:var(--cb-space-2) auto calc(-2rem - var(--cb-space-2));padding:0 var(--cb-space-3);overflow:hidden;border-radius:var(--cb-radius-full);background:var(--cb-color-surface-950, #0f172a);color:#fff;font-size:var(--cb-text-xs);line-height:2rem;text-overflow:ellipsis;white-space:nowrap;box-shadow:var(--cb-shadow-md)}.cb-pdf__marks{position:absolute;inset:0;z-index:2;pointer-events:none}.cb-pdf__marks--capture{pointer-events:auto;cursor:cell;touch-action:none}.cb-pdf__marks--ink{cursor:crosshair}.cb-pdf__mark{position:absolute;box-sizing:border-box;pointer-events:auto;border-radius:2px;outline-offset:2px;touch-action:none}.cb-pdf__mark:focus-visible,.cb-pdf__mark.cb-is-selected{outline:2px solid var(--cb-color-focus-ring)}.cb-pdf__marks--pass .cb-pdf__mark--highlight{pointer-events:none}.cb-pdf__mark--highlight{cursor:pointer}.cb-pdf__hl-rect{position:absolute;background:color-mix(in srgb,var(--cb-pdf-mark-light) 55%,transparent)}.cb-pdf__hl-badge{position:absolute;top:-.35rem;right:-.35rem;width:.625rem;height:.625rem;border:2px solid #fff;border-radius:var(--cb-radius-full);background:var(--cb-pdf-mark-dark)}.cb-pdf__mark--note,.cb-pdf__mark--signature,.cb-pdf__mark--ink{cursor:move}.cb-pdf__note-text{display:block;box-sizing:border-box;padding:.1em .3em;border:1px dashed color-mix(in srgb,var(--cb-pdf-mark-dark) 45%,transparent);border-radius:2px;background:transparent;color:var(--cb-pdf-mark-dark);font-family:Helvetica,Arial,sans-serif;line-height:1.2;white-space:pre}.cb-pdf__ink,.cb-pdf__stroke{position:absolute;inset:0;width:100%;height:100%;overflow:visible;pointer-events:none}.cb-pdf__ink path,.cb-pdf__stroke polyline{fill:none;stroke:var(--cb-pdf-mark-dark);stroke-linecap:round;stroke-linejoin:round}.cb-pdf__sig{display:block;width:100%;height:100%;-webkit-user-select:none;user-select:none;-webkit-user-drag:none}.cb-pdf__resize{position:absolute;right:-.4rem;bottom:-.4rem;width:.8rem;height:.8rem;border:2px solid #fff;border-radius:2px;background:var(--cb-color-focus-ring);cursor:nwse-resize}.cb-pdf__card{position:absolute;z-index:3;display:flex;flex-direction:column;gap:var(--cb-space-2);box-sizing:border-box;width:16rem;padding:var(--cb-space-3);border:var(--cb-border-width, 1px) solid var(--cb-color-border);border-radius:var(--cb-radius-md);background:var(--cb-color-surface-raised, var(--cb-color-surface));color:var(--cb-color-text);font-size:var(--cb-text-sm);box-shadow:var(--cb-shadow-lg);pointer-events:auto;cursor:auto}.cb-pdf__card-label{font-weight:var(--cb-font-weight-medium)}.cb-pdf__card-text{height:auto;min-height:4.5rem;padding:var(--cb-space-2);resize:vertical}.cb-pdf__card-actions,.cb-pdf__sign-actions{display:flex;justify-content:flex-end;gap:var(--cb-space-2)}.cb-pdf__card-btn{height:2rem;padding:0 var(--cb-space-3);border:var(--cb-border-width, 1px) solid var(--cb-color-border-strong);border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text);font:inherit;cursor:pointer}.cb-pdf__card-btn:hover:not(:disabled){background:var(--cb-color-surface-hover)}.cb-pdf__card-btn--primary{border-color:var(--cb-color-primary-500);background:var(--cb-color-primary-500);color:var(--cb-color-primary-contrast);font-weight:var(--cb-font-weight-semibold)}.cb-pdf__card-btn--primary:hover:not(:disabled){background:var(--cb-color-primary-600)}.cb-pdf__card-btn:disabled{opacity:var(--cb-opacity-disabled, .5);cursor:default}.cb-pdf__card-btn:focus-visible,.cb-pdf__card-text:focus-visible,.cb-pdf__notes-link:focus-visible,.cb-pdf__notes-remove:focus-visible{outline:2px solid var(--cb-color-focus-ring);outline-offset:2px}.cb-pdf__notes-panel{padding:0 var(--cb-space-2)}.cb-pdf__notes-empty{margin:0;padding:var(--cb-space-2);color:var(--cb-color-text-muted)}.cb-pdf__notes-list{display:flex;flex-direction:column;gap:2px;margin:0;padding:0;list-style:none}.cb-pdf__notes-item{display:flex;align-items:flex-start;gap:2px}.cb-pdf__notes-link{display:flex;flex:1;align-items:flex-start;gap:var(--cb-space-2);min-width:0;padding:var(--cb-space-2);border:0;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text);font:inherit;text-align:left;cursor:pointer}.cb-pdf__notes-link:hover,.cb-pdf__notes-remove:hover{background:var(--cb-color-surface-hover)}.cb-pdf__notes-swatch{flex:none;width:.625rem;height:.625rem;margin-top:.3rem;border-radius:var(--cb-radius-full)}.cb-pdf__notes-text{display:flex;flex-direction:column;min-width:0}.cb-pdf__notes-page{color:var(--cb-color-text-muted);font-size:var(--cb-text-xs)}.cb-pdf__notes-label{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow-wrap:anywhere}.cb-pdf__notes-remove{display:inline-flex;flex:none;align-items:center;justify-content:center;width:2rem;height:2rem;border:0;border-radius:var(--cb-radius-md);background:transparent;color:var(--cb-color-text-muted);cursor:pointer}.cb-pdf__sr{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}@media(forced-colors:active){.cb-pdf__hl-rect{background:transparent;outline:2px solid Mark}.cb-pdf__note-text{border-color:CanvasText}}\n"] }]
3218
+ }], ctorParameters: () => [], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], fileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "fileName", required: false }] }], password: [{ type: i0.Input, args: [{ isSignal: true, alias: "password", required: false }] }], withCredentials: [{ type: i0.Input, args: [{ isSignal: true, alias: "withCredentials", required: false }] }], httpHeaders: [{ type: i0.Input, args: [{ isSignal: true, alias: "httpHeaders", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }], zoom: [{ type: i0.Input, args: [{ isSignal: true, alias: "zoom", required: false }] }, { type: i0.Output, args: ["zoomChange"] }], rotation: [{ type: i0.Input, args: [{ isSignal: true, alias: "rotation", required: false }] }, { type: i0.Output, args: ["rotationChange"] }], sidebar: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebar", required: false }] }, { type: i0.Output, args: ["sidebarChange"] }], showToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showToolbar", required: false }] }], showSearch: [{ type: i0.Input, args: [{ isSignal: true, alias: "showSearch", required: false }] }], showThumbnails: [{ type: i0.Input, args: [{ isSignal: true, alias: "showThumbnails", required: false }] }], showRotate: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRotate", required: false }] }], downloadable: [{ type: i0.Input, args: [{ isSignal: true, alias: "downloadable", required: false }] }], printable: [{ type: i0.Input, args: [{ isSignal: true, alias: "printable", required: false }] }], showOutline: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOutline", required: false }] }], forms: [{ type: i0.Input, args: [{ isSignal: true, alias: "forms", required: false }] }], externalLinks: [{ type: i0.Input, args: [{ isSignal: true, alias: "externalLinks", required: false }] }], sidebarView: [{ type: i0.Input, args: [{ isSignal: true, alias: "sidebarView", required: false }] }, { type: i0.Output, args: ["sidebarViewChange"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], annotatable: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotatable", required: false }] }], annotations: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotations", required: false }] }, { type: i0.Output, args: ["annotationsChange"] }], tool: [{ type: i0.Input, args: [{ isSignal: true, alias: "tool", required: false }] }, { type: i0.Output, args: ["toolChange"] }], annotationColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "annotationColor", required: false }] }, { type: i0.Output, args: ["annotationColorChange"] }], author: [{ type: i0.Input, args: [{ isSignal: true, alias: "author", required: false }] }], loaded: [{ type: i0.Output, args: ["loaded"] }], loadError: [{ type: i0.Output, args: ["loadError"] }], formChange: [{ type: i0.Output, args: ["formChange"] }], annotationAdd: [{ type: i0.Output, args: ["annotationAdd"] }], annotationRemove: [{ type: i0.Output, args: ["annotationRemove"] }], viewport: [{ type: i0.ViewChild, args: ['viewport', { isSignal: true }] }], pageEls: [{ type: i0.ViewChildren, args: ['pageEl', { isSignal: true }] }], thumbEls: [{ type: i0.ViewChildren, args: ['thumbEl', { isSignal: true }] }], searchInput: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }], passwordInput: [{ type: i0.ViewChild, args: ['passwordInput', { isSignal: true }] }], cardText: [{ type: i0.ViewChild, args: ['cardText', { isSignal: true }] }] } });
1507
3219
  function sizeOf(page) {
1508
3220
  const viewport = page.getViewport({ scale: 1, rotation: 0 });
1509
3221
  const userUnit = page.userUnit || 1;
1510
- return { width: viewport.width, height: viewport.height, rotate: page.rotate || 0, userUnit };
3222
+ const [x0 = 0, y0 = 0] = page.view ?? [];
3223
+ return { x0, y0, width: viewport.width, height: viewport.height, rotate: page.rotate || 0, userUnit };
1511
3224
  }
1512
3225
  /** Bytes del documento si no es una URL (copiados: pdf.js transfiere el buffer al worker). */
1513
3226
  async function toData(src) {
@@ -1541,6 +3254,23 @@ function clamp(value, min, max) {
1541
3254
  function round2(value) {
1542
3255
  return Math.round(value * 100) / 100;
1543
3256
  }
3257
+ /** Quita de una selección las cajas mucho más altas que una línea (contenedores, fin de contenido). */
3258
+ function withoutTallRects(rects) {
3259
+ if (rects.length < 2)
3260
+ return rects;
3261
+ const heights = rects.map((r) => r.height).sort((a, b) => a - b);
3262
+ const median = heights[Math.floor(heights.length / 2)];
3263
+ return rects.filter((r) => r.height <= median * 2.5);
3264
+ }
3265
+ function unionBox(boxes) {
3266
+ const left = Math.min(...boxes.map((b) => b.left));
3267
+ const top = Math.min(...boxes.map((b) => b.top));
3268
+ return { left, top, width: Math.max(...boxes.map((b) => b.left + b.width)) - left, height: Math.max(...boxes.map((b) => b.top + b.height)) - top };
3269
+ }
3270
+ function truncate(text, max) {
3271
+ const clean = text.replace(/\s+/g, ' ').trim();
3272
+ return clean.length > max ? `${clean.slice(0, max - 1)}…` : clean;
3273
+ }
1544
3274
 
1545
3275
  /*
1546
3276
  * Public API Surface de @caribeng/ui/pdfviewer
@@ -1550,4 +3280,4 @@ function round2(value) {
1550
3280
  * Generated bundle index. Do not edit.
1551
3281
  */
1552
3282
 
1553
- export { CB_PDF_VIEWER_OPTIONS, CbPdfEngine, CbPdfViewer, provideCbPdfViewer };
3283
+ export { CB_PDF_COLORS, CB_PDF_VIEWER_OPTIONS, CbPdfEngine, CbPdfViewer, provideCbPdfViewer };