@caribeng/ui 22.1.1 → 22.4.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 (305) hide show
  1. package/README.md +134 -12
  2. package/fesm2022/caribeng-ui-accordion.mjs +4 -4
  3. package/fesm2022/caribeng-ui-anchor.mjs +169 -0
  4. package/fesm2022/caribeng-ui-autocomplete.mjs +10 -8
  5. package/fesm2022/caribeng-ui-avatar.mjs +2 -2
  6. package/fesm2022/caribeng-ui-avatargroup.mjs +9 -3
  7. package/fesm2022/caribeng-ui-badge.mjs +8 -2
  8. package/fesm2022/caribeng-ui-blockui.mjs +12 -4
  9. package/fesm2022/caribeng-ui-breadcrumb.mjs +17 -7
  10. package/fesm2022/caribeng-ui-button.mjs +22 -2
  11. package/fesm2022/caribeng-ui-caribe-alert.mjs +5 -2
  12. package/fesm2022/caribeng-ui-caribe-animation-lottie.mjs +6 -3
  13. package/fesm2022/caribeng-ui-caribe-button-group.mjs +6 -3
  14. package/fesm2022/caribeng-ui-caribe-calendar.mjs +11 -6
  15. package/fesm2022/caribeng-ui-caribe-card-content.mjs +6 -3
  16. package/fesm2022/caribeng-ui-caribe-card-move.mjs +29 -7
  17. package/fesm2022/caribeng-ui-caribe-card-select.mjs +56 -14
  18. package/fesm2022/caribeng-ui-caribe-circle-loading.mjs +10 -7
  19. package/fesm2022/caribeng-ui-caribe-coachmark.mjs +42 -19
  20. package/fesm2022/caribeng-ui-caribe-data-table.mjs +15 -9
  21. package/fesm2022/caribeng-ui-caribe-dots-indicator.mjs +38 -5
  22. package/fesm2022/caribeng-ui-caribe-expansion-panel.mjs +16 -5
  23. package/fesm2022/caribeng-ui-caribe-fab-menu.mjs +56 -7
  24. package/fesm2022/caribeng-ui-caribe-file-download.mjs +68 -27
  25. package/fesm2022/caribeng-ui-caribe-file-select.mjs +22 -17
  26. package/fesm2022/caribeng-ui-caribe-float-menu.mjs +6 -3
  27. package/fesm2022/caribeng-ui-caribe-footer.mjs +8 -4
  28. package/fesm2022/caribeng-ui-caribe-header.mjs +6 -3
  29. package/fesm2022/caribeng-ui-caribe-icon-button.mjs +6 -3
  30. package/fesm2022/caribeng-ui-caribe-illustration.mjs +6 -3
  31. package/fesm2022/caribeng-ui-caribe-inline-alert.mjs +5 -2
  32. package/fesm2022/caribeng-ui-caribe-input-search.mjs +14 -10
  33. package/fesm2022/caribeng-ui-caribe-input-select.mjs +12 -10
  34. package/fesm2022/caribeng-ui-caribe-input-token.mjs +61 -11
  35. package/fesm2022/caribeng-ui-caribe-link.mjs +7 -2
  36. package/fesm2022/caribeng-ui-caribe-loader.mjs +8 -5
  37. package/fesm2022/caribeng-ui-caribe-modal.mjs +32 -16
  38. package/fesm2022/caribeng-ui-caribe-off-canvas.mjs +26 -12
  39. package/fesm2022/caribeng-ui-caribe-onboarding.mjs +44 -19
  40. package/fesm2022/caribeng-ui-caribe-pictogram.mjs +6 -3
  41. package/fesm2022/caribeng-ui-caribe-pop-over.mjs +34 -10
  42. package/fesm2022/caribeng-ui-caribe-preloader.mjs +9 -6
  43. package/fesm2022/caribeng-ui-caribe-progress-bar.mjs +5 -2
  44. package/fesm2022/caribeng-ui-caribe-shortcut.mjs +8 -3
  45. package/fesm2022/caribeng-ui-caribe-step-indicator.mjs +12 -5
  46. package/fesm2022/caribeng-ui-caribe-stroke.mjs +6 -3
  47. package/fesm2022/caribeng-ui-caribe-switch.mjs +5 -3
  48. package/fesm2022/caribeng-ui-caribe-tag-button.mjs +1 -1
  49. package/fesm2022/caribeng-ui-caribe-tracker.mjs +5 -2
  50. package/fesm2022/caribeng-ui-caribe-transaction-status.mjs +1 -1
  51. package/fesm2022/caribeng-ui-caribe-voucher.mjs +11 -8
  52. package/fesm2022/caribeng-ui-carousel.mjs +49 -14
  53. package/fesm2022/caribeng-ui-cascadeselect.mjs +22 -8
  54. package/fesm2022/caribeng-ui-chart.mjs +2601 -303
  55. package/fesm2022/caribeng-ui-chat.mjs +1303 -0
  56. package/fesm2022/caribeng-ui-chip.mjs +9 -4
  57. package/fesm2022/caribeng-ui-chips.mjs +9 -5
  58. package/fesm2022/caribeng-ui-code-block.mjs +332 -0
  59. package/fesm2022/caribeng-ui-colorpicker.mjs +107 -11
  60. package/fesm2022/caribeng-ui-command-palette.mjs +476 -0
  61. package/fesm2022/caribeng-ui-confirmdialog.mjs +140 -10
  62. package/fesm2022/caribeng-ui-confirmpopup.mjs +209 -9
  63. package/fesm2022/caribeng-ui-contextmenu.mjs +13 -6
  64. package/fesm2022/caribeng-ui-copy-button.mjs +230 -0
  65. package/fesm2022/caribeng-ui-dataview.mjs +214 -6
  66. package/fesm2022/caribeng-ui-datepicker.mjs +1630 -393
  67. package/fesm2022/caribeng-ui-descriptions.mjs +124 -0
  68. package/fesm2022/caribeng-ui-dialog.mjs +10 -7
  69. package/fesm2022/caribeng-ui-dock.mjs +58 -15
  70. package/fesm2022/caribeng-ui-drawer.mjs +17 -9
  71. package/fesm2022/caribeng-ui-dynamicdialog.mjs +210 -6
  72. package/fesm2022/caribeng-ui-editor.mjs +4413 -243
  73. package/fesm2022/caribeng-ui-empty.mjs +80 -0
  74. package/fesm2022/caribeng-ui-fieldset.mjs +2 -2
  75. package/fesm2022/caribeng-ui-file-card.mjs +136 -19
  76. package/fesm2022/caribeng-ui-fileupload.mjs +96 -19
  77. package/fesm2022/caribeng-ui-floatlabel.mjs +4 -2
  78. package/fesm2022/caribeng-ui-folder-card.mjs +132 -15
  79. package/fesm2022/caribeng-ui-galleria.mjs +76 -45
  80. package/fesm2022/caribeng-ui-image.mjs +52 -29
  81. package/fesm2022/caribeng-ui-inlinemessage.mjs +2 -2
  82. package/fesm2022/caribeng-ui-inplace.mjs +22 -6
  83. package/fesm2022/caribeng-ui-input-number.mjs +9 -7
  84. package/fesm2022/caribeng-ui-inputgroup.mjs +2 -2
  85. package/fesm2022/caribeng-ui-inputmask.mjs +22 -13
  86. package/fesm2022/caribeng-ui-inputotp.mjs +12 -6
  87. package/fesm2022/caribeng-ui-kanban.mjs +4612 -0
  88. package/fesm2022/caribeng-ui-kbd.mjs +122 -0
  89. package/fesm2022/caribeng-ui-keyfilter.mjs +72 -0
  90. package/fesm2022/caribeng-ui-listbox.mjs +13 -11
  91. package/fesm2022/caribeng-ui-megamenu.mjs +228 -32
  92. package/fesm2022/caribeng-ui-mentions.mjs +495 -0
  93. package/fesm2022/caribeng-ui-menu.mjs +18 -8
  94. package/fesm2022/caribeng-ui-menubar.mjs +17 -8
  95. package/fesm2022/caribeng-ui-message.mjs +13 -7
  96. package/fesm2022/caribeng-ui-metergroup.mjs +29 -11
  97. package/fesm2022/caribeng-ui-multiselect.mjs +399 -97
  98. package/fesm2022/caribeng-ui-orderlist.mjs +230 -11
  99. package/fesm2022/caribeng-ui-organizationchart.mjs +16 -15
  100. package/fesm2022/caribeng-ui-paginator.mjs +18 -15
  101. package/fesm2022/caribeng-ui-panel.mjs +11 -8
  102. package/fesm2022/caribeng-ui-panelmenu.mjs +14 -8
  103. package/fesm2022/caribeng-ui-password.mjs +8 -6
  104. package/fesm2022/caribeng-ui-pdfviewer.mjs +1553 -0
  105. package/fesm2022/caribeng-ui-phone-input.mjs +688 -0
  106. package/fesm2022/caribeng-ui-picklist.mjs +251 -14
  107. package/fesm2022/caribeng-ui-pricing.mjs +272 -75
  108. package/fesm2022/caribeng-ui-qrcode.mjs +564 -0
  109. package/fesm2022/caribeng-ui-rating.mjs +7 -3
  110. package/fesm2022/caribeng-ui-repeater.mjs +223 -0
  111. package/fesm2022/caribeng-ui-result.mjs +91 -0
  112. package/fesm2022/caribeng-ui-ripple.mjs +2 -0
  113. package/fesm2022/caribeng-ui-scrollpanel.mjs +6 -3
  114. package/fesm2022/caribeng-ui-scrolltop.mjs +9 -4
  115. package/fesm2022/caribeng-ui-select.mjs +379 -87
  116. package/fesm2022/caribeng-ui-selectbutton.mjs +2 -2
  117. package/fesm2022/caribeng-ui-sidebar.mjs +573 -0
  118. package/fesm2022/caribeng-ui-slider.mjs +171 -75
  119. package/fesm2022/caribeng-ui-speeddial.mjs +7 -4
  120. package/fesm2022/caribeng-ui-spinner.mjs +5 -2
  121. package/fesm2022/caribeng-ui-splitbutton.mjs +163 -0
  122. package/fesm2022/caribeng-ui-splitter.mjs +5 -2
  123. package/fesm2022/caribeng-ui-statistic.mjs +183 -0
  124. package/fesm2022/caribeng-ui-stepper.mjs +17 -7
  125. package/fesm2022/caribeng-ui-steps.mjs +6 -3
  126. package/fesm2022/caribeng-ui-table.mjs +716 -84
  127. package/fesm2022/caribeng-ui-tabmenu.mjs +13 -6
  128. package/fesm2022/caribeng-ui-tag.mjs +2 -2
  129. package/fesm2022/caribeng-ui-terminal.mjs +45 -9
  130. package/fesm2022/caribeng-ui-themetoggle.mjs +82 -0
  131. package/fesm2022/caribeng-ui-tieredmenu.mjs +113 -20
  132. package/fesm2022/caribeng-ui-timeline.mjs +175 -6
  133. package/fesm2022/caribeng-ui-toast.mjs +7 -4
  134. package/fesm2022/caribeng-ui-togglebutton.mjs +8 -6
  135. package/fesm2022/caribeng-ui-toolbar.mjs +6 -3
  136. package/fesm2022/caribeng-ui-tree.mjs +302 -6
  137. package/fesm2022/caribeng-ui-treeselect.mjs +26 -10
  138. package/fesm2022/caribeng-ui-treetable.mjs +269 -6
  139. package/fesm2022/caribeng-ui-virtualscroller.mjs +123 -10
  140. package/fesm2022/caribeng-ui.mjs +1216 -6
  141. package/package.json +93 -3
  142. package/schematics/collection.json +15 -0
  143. package/schematics/migrate-from-primeng/core-icons.json +1 -0
  144. package/schematics/migrate-from-primeng/icons.js +269 -0
  145. package/schematics/migrate-from-primeng/index.js +290 -0
  146. package/schematics/migrate-from-primeng/mappings/button.js +93 -0
  147. package/schematics/migrate-from-primeng/mappings/card.js +33 -0
  148. package/schematics/migrate-from-primeng/mappings/checkbox.js +44 -0
  149. package/schematics/migrate-from-primeng/mappings/confirm.js +221 -0
  150. package/schematics/migrate-from-primeng/mappings/datepicker.js +121 -0
  151. package/schematics/migrate-from-primeng/mappings/dialog.js +84 -0
  152. package/schematics/migrate-from-primeng/mappings/index.js +88 -0
  153. package/schematics/migrate-from-primeng/mappings/inputnumber.js +62 -0
  154. package/schematics/migrate-from-primeng/mappings/inputtext.js +60 -0
  155. package/schematics/migrate-from-primeng/mappings/menu.js +468 -0
  156. package/schematics/migrate-from-primeng/mappings/multiselect.js +49 -0
  157. package/schematics/migrate-from-primeng/mappings/radiobutton.js +22 -0
  158. package/schematics/migrate-from-primeng/mappings/rules.js +206 -0
  159. package/schematics/migrate-from-primeng/mappings/select.js +97 -0
  160. package/schematics/migrate-from-primeng/mappings/service-calls.js +100 -0
  161. package/schematics/migrate-from-primeng/mappings/table.js +613 -0
  162. package/schematics/migrate-from-primeng/mappings/tabs.js +225 -0
  163. package/schematics/migrate-from-primeng/mappings/tag.js +29 -0
  164. package/schematics/migrate-from-primeng/mappings/textarea.js +44 -0
  165. package/schematics/migrate-from-primeng/mappings/toast.js +173 -0
  166. package/schematics/migrate-from-primeng/mappings/toggleswitch.js +37 -0
  167. package/schematics/migrate-from-primeng/mappings/tooltip.js +44 -0
  168. package/schematics/migrate-from-primeng/mappings/types.js +10 -0
  169. package/schematics/migrate-from-primeng/report.js +79 -0
  170. package/schematics/migrate-from-primeng/schema.js +2 -0
  171. package/schematics/migrate-from-primeng/schema.json +34 -0
  172. package/schematics/migrate-from-primeng/template.js +387 -0
  173. package/schematics/migrate-from-primeng/todo.js +5 -0
  174. package/schematics/migrate-from-primeng/typescript.js +442 -0
  175. package/schematics/migrate-from-primeng/workspace.js +117 -0
  176. package/schematics/ng-add/index.js +140 -0
  177. package/schematics/ng-add/schema.js +2 -0
  178. package/schematics/ng-add/schema.json +66 -0
  179. package/types/caribeng-ui-anchor.d.ts +50 -0
  180. package/types/caribeng-ui-autocomplete.d.ts +4 -2
  181. package/types/caribeng-ui-avatargroup.d.ts +3 -1
  182. package/types/caribeng-ui-badge.d.ts +3 -1
  183. package/types/caribeng-ui-blockui.d.ts +9 -4
  184. package/types/caribeng-ui-breadcrumb.d.ts +11 -0
  185. package/types/caribeng-ui-button.d.ts +9 -1
  186. package/types/caribeng-ui-caribe-alert.d.ts +3 -0
  187. package/types/caribeng-ui-caribe-animation-lottie.d.ts +4 -1
  188. package/types/caribeng-ui-caribe-button-group.d.ts +4 -1
  189. package/types/caribeng-ui-caribe-calendar.d.ts +5 -1
  190. package/types/caribeng-ui-caribe-card-content.d.ts +4 -1
  191. package/types/caribeng-ui-caribe-card-move.d.ts +13 -13
  192. package/types/caribeng-ui-caribe-card-select.d.ts +10 -1
  193. package/types/caribeng-ui-caribe-circle-loading.d.ts +4 -1
  194. package/types/caribeng-ui-caribe-coachmark.d.ts +6 -0
  195. package/types/caribeng-ui-caribe-data-table.d.ts +7 -2
  196. package/types/caribeng-ui-caribe-dots-indicator.d.ts +9 -0
  197. package/types/caribeng-ui-caribe-expansion-panel.d.ts +7 -10
  198. package/types/caribeng-ui-caribe-fab-menu.d.ts +10 -1
  199. package/types/caribeng-ui-caribe-file-download.d.ts +8 -10
  200. package/types/caribeng-ui-caribe-file-select.d.ts +7 -1
  201. package/types/caribeng-ui-caribe-float-menu.d.ts +4 -1
  202. package/types/caribeng-ui-caribe-header.d.ts +3 -0
  203. package/types/caribeng-ui-caribe-icon-button.d.ts +4 -1
  204. package/types/caribeng-ui-caribe-illustration.d.ts +4 -1
  205. package/types/caribeng-ui-caribe-inline-alert.d.ts +3 -0
  206. package/types/caribeng-ui-caribe-input-search.d.ts +4 -2
  207. package/types/caribeng-ui-caribe-input-select.d.ts +4 -2
  208. package/types/caribeng-ui-caribe-input-token.d.ts +16 -1
  209. package/types/caribeng-ui-caribe-link.d.ts +3 -0
  210. package/types/caribeng-ui-caribe-loader.d.ts +4 -1
  211. package/types/caribeng-ui-caribe-modal.d.ts +7 -10
  212. package/types/caribeng-ui-caribe-off-canvas.d.ts +6 -1
  213. package/types/caribeng-ui-caribe-onboarding.d.ts +6 -0
  214. package/types/caribeng-ui-caribe-pictogram.d.ts +4 -1
  215. package/types/caribeng-ui-caribe-pop-over.d.ts +7 -10
  216. package/types/caribeng-ui-caribe-preloader.d.ts +4 -1
  217. package/types/caribeng-ui-caribe-progress-bar.d.ts +3 -0
  218. package/types/caribeng-ui-caribe-shortcut.d.ts +3 -1
  219. package/types/caribeng-ui-caribe-step-indicator.d.ts +6 -0
  220. package/types/caribeng-ui-caribe-stroke.d.ts +4 -1
  221. package/types/caribeng-ui-caribe-switch.d.ts +3 -1
  222. package/types/caribeng-ui-caribe-tracker.d.ts +3 -0
  223. package/types/caribeng-ui-caribe-voucher.d.ts +5 -2
  224. package/types/caribeng-ui-carousel.d.ts +10 -1
  225. package/types/caribeng-ui-cascadeselect.d.ts +7 -2
  226. package/types/caribeng-ui-chart.d.ts +471 -44
  227. package/types/caribeng-ui-chat.d.ts +299 -0
  228. package/types/caribeng-ui-chip.d.ts +6 -0
  229. package/types/caribeng-ui-chips.d.ts +6 -2
  230. package/types/caribeng-ui-code-block.d.ts +65 -0
  231. package/types/caribeng-ui-colorpicker.d.ts +8 -2
  232. package/types/caribeng-ui-command-palette.d.ts +133 -0
  233. package/types/caribeng-ui-confirmdialog.d.ts +57 -6
  234. package/types/caribeng-ui-confirmpopup.d.ts +74 -5
  235. package/types/caribeng-ui-contextmenu.d.ts +11 -1
  236. package/types/caribeng-ui-copy-button.d.ts +90 -0
  237. package/types/caribeng-ui-dataview.d.ts +65 -5
  238. package/types/caribeng-ui-datepicker.d.ts +186 -21
  239. package/types/caribeng-ui-descriptions.d.ts +63 -0
  240. package/types/caribeng-ui-dialog.d.ts +4 -1
  241. package/types/caribeng-ui-dock.d.ts +17 -2
  242. package/types/caribeng-ui-drawer.d.ts +7 -2
  243. package/types/caribeng-ui-dynamicdialog.d.ts +93 -3
  244. package/types/caribeng-ui-editor.d.ts +486 -13
  245. package/types/caribeng-ui-empty.d.ts +34 -0
  246. package/types/caribeng-ui-file-card.d.ts +28 -3
  247. package/types/caribeng-ui-fileupload.d.ts +27 -4
  248. package/types/caribeng-ui-folder-card.d.ts +31 -6
  249. package/types/caribeng-ui-galleria.d.ts +9 -0
  250. package/types/caribeng-ui-image.d.ts +10 -0
  251. package/types/caribeng-ui-inplace.d.ts +8 -0
  252. package/types/caribeng-ui-input-number.d.ts +3 -1
  253. package/types/caribeng-ui-inputmask.d.ts +8 -1
  254. package/types/caribeng-ui-inputotp.d.ts +7 -2
  255. package/types/caribeng-ui-kanban.d.ts +783 -0
  256. package/types/caribeng-ui-kbd.d.ts +36 -0
  257. package/types/caribeng-ui-keyfilter.d.ts +30 -0
  258. package/types/caribeng-ui-listbox.d.ts +5 -3
  259. package/types/caribeng-ui-megamenu.d.ts +37 -2
  260. package/types/caribeng-ui-mentions.d.ts +127 -0
  261. package/types/caribeng-ui-menu.d.ts +14 -2
  262. package/types/caribeng-ui-menubar.d.ts +12 -1
  263. package/types/caribeng-ui-message.d.ts +5 -0
  264. package/types/caribeng-ui-metergroup.d.ts +9 -1
  265. package/types/caribeng-ui-multiselect.d.ts +71 -10
  266. package/types/caribeng-ui-orderlist.d.ts +58 -6
  267. package/types/caribeng-ui-organizationchart.d.ts +7 -1
  268. package/types/caribeng-ui-paginator.d.ts +3 -0
  269. package/types/caribeng-ui-panel.d.ts +3 -0
  270. package/types/caribeng-ui-panelmenu.d.ts +11 -1
  271. package/types/caribeng-ui-password.d.ts +4 -2
  272. package/types/caribeng-ui-pdfviewer.d.ts +258 -0
  273. package/types/caribeng-ui-phone-input.d.ts +168 -0
  274. package/types/caribeng-ui-picklist.d.ts +67 -7
  275. package/types/caribeng-ui-pricing.d.ts +49 -9
  276. package/types/caribeng-ui-qrcode.d.ts +88 -0
  277. package/types/caribeng-ui-rating.d.ts +6 -2
  278. package/types/caribeng-ui-repeater.d.ts +79 -0
  279. package/types/caribeng-ui-result.d.ts +35 -0
  280. package/types/caribeng-ui-scrollpanel.d.ts +4 -1
  281. package/types/caribeng-ui-scrolltop.d.ts +4 -1
  282. package/types/caribeng-ui-select.d.ts +73 -9
  283. package/types/caribeng-ui-sidebar.d.ts +167 -0
  284. package/types/caribeng-ui-slider.d.ts +63 -10
  285. package/types/caribeng-ui-speeddial.d.ts +5 -2
  286. package/types/caribeng-ui-spinner.d.ts +3 -0
  287. package/types/caribeng-ui-splitbutton.d.ts +49 -0
  288. package/types/caribeng-ui-splitter.d.ts +3 -0
  289. package/types/caribeng-ui-statistic.d.ts +65 -0
  290. package/types/caribeng-ui-stepper.d.ts +5 -0
  291. package/types/caribeng-ui-steps.d.ts +4 -1
  292. package/types/caribeng-ui-table.d.ts +185 -17
  293. package/types/caribeng-ui-tabmenu.d.ts +11 -1
  294. package/types/caribeng-ui-terminal.d.ts +10 -0
  295. package/types/caribeng-ui-themetoggle.d.ts +32 -0
  296. package/types/caribeng-ui-tieredmenu.d.ts +22 -3
  297. package/types/caribeng-ui-timeline.d.ts +63 -4
  298. package/types/caribeng-ui-toast.d.ts +3 -0
  299. package/types/caribeng-ui-togglebutton.d.ts +5 -3
  300. package/types/caribeng-ui-toolbar.d.ts +7 -4
  301. package/types/caribeng-ui-tree.d.ts +76 -4
  302. package/types/caribeng-ui-treeselect.d.ts +10 -2
  303. package/types/caribeng-ui-treetable.d.ts +68 -4
  304. package/types/caribeng-ui-virtualscroller.d.ts +45 -5
  305. package/types/caribeng-ui.d.ts +775 -12
@@ -0,0 +1,1553 @@
1
+ import { DOCUMENT, isPlatformBrowser } from '@angular/common';
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';
4
+ import { injectCbTranslation, cbFormat } from '@caribeng/ui';
5
+ import { CbIconRegistry, CbIcon } from '@caribeng/ui/icon';
6
+ import { panelLeft, printer, zoomOut, zoomIn } from '@caribeng/ui/icons';
7
+ import { CbSpinner } from '@caribeng/ui/spinner';
8
+
9
+ const CB_PDF_VIEWER_OPTIONS = new InjectionToken('CB_PDF_VIEWER_OPTIONS');
10
+ /**
11
+ * Configura el visor de PDF (worker, recursos y carga de pdf.js).
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * bootstrapApplication(App, {
16
+ * providers: [provideCaribeNG(), provideCbPdfViewer({ workerSrc: '/static/pdf.worker.min.mjs' })],
17
+ * });
18
+ * ```
19
+ */
20
+ function provideCbPdfViewer(options) {
21
+ return makeEnvironmentProviders([{ provide: CB_PDF_VIEWER_OPTIONS, useValue: options }]);
22
+ }
23
+ /**
24
+ * Carga pdf.js una sola vez y configura su worker. Las URLs se resuelven contra
25
+ * el `<base href>` del documento: el worker de pdf.js las pediría relativas a
26
+ * su propio script.
27
+ */
28
+ class CbPdfEngine {
29
+ constructor() {
30
+ this.options = inject(CB_PDF_VIEWER_OPTIONS, { optional: true }) ?? {};
31
+ this.document = inject(DOCUMENT);
32
+ }
33
+ load() {
34
+ this.pdfjs ??= (this.options.loader ?? (() => import('pdfjs-dist/legacy/build/pdf.mjs')))().then((pdfjs) => {
35
+ if (pdfjs.GlobalWorkerOptions && !pdfjs.GlobalWorkerOptions.workerSrc && !pdfjs.GlobalWorkerOptions.workerPort) {
36
+ pdfjs.GlobalWorkerOptions.workerSrc = this.resolve(this.options.workerSrc ?? 'pdfjs/build/pdf.worker.min.mjs');
37
+ }
38
+ return pdfjs;
39
+ });
40
+ // Si falla (p. ej. pdfjs-dist no está instalado) se puede reintentar.
41
+ this.pdfjs.catch(() => (this.pdfjs = undefined));
42
+ return this.pdfjs;
43
+ }
44
+ resources() {
45
+ const base = this.resolve(withSlash(this.options.assetsUrl ?? 'pdfjs/'));
46
+ return {
47
+ cMapUrl: `${base}cmaps/`,
48
+ standardFontDataUrl: `${base}standard_fonts/`,
49
+ wasmUrl: `${base}wasm/`,
50
+ iccUrl: `${base}iccs/`,
51
+ };
52
+ }
53
+ /**
54
+ * Resuelve una URL contra el `<base href>`, como Angular y `<img>`; pdf.js
55
+ * la resolvería contra la ruta actual (`/components/visor/doc.pdf`).
56
+ */
57
+ resolve(url) {
58
+ try {
59
+ return new URL(url, this.document.baseURI).href;
60
+ }
61
+ catch {
62
+ return String(url);
63
+ }
64
+ }
65
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbPdfEngine, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
66
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbPdfEngine, providedIn: 'root' }); }
67
+ }
68
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbPdfEngine, decorators: [{
69
+ type: Injectable,
70
+ args: [{ providedIn: 'root' }]
71
+ }] });
72
+ function withSlash(url) {
73
+ return url.endsWith('/') ? url : `${url}/`;
74
+ }
75
+
76
+ /** Resolución de impresión (ppp). */
77
+ const PRINT_DPI = 150;
78
+ /**
79
+ * Imprime el documento: rasteriza cada página a {@link PRINT_DPI} ppp en un
80
+ * `<iframe>` oculto y abre el diálogo de impresión del navegador (así se
81
+ * imprime el PDF y no la página de la aplicación).
82
+ */
83
+ async function printPdfDocument(pdf, document, onProgress, rotation = 0) {
84
+ const urls = [];
85
+ const frame = document.createElement('iframe');
86
+ frame.setAttribute('aria-hidden', 'true');
87
+ frame.tabIndex = -1;
88
+ frame.style.cssText = 'position:fixed;right:0;bottom:0;width:0;height:0;border:0;visibility:hidden';
89
+ try {
90
+ let pageSize = '';
91
+ for (let n = 1; n <= pdf.numPages; n++) {
92
+ const page = await pdf.getPage(n);
93
+ const viewport = page.getViewport({ scale: PRINT_DPI / 72, rotation: (page.rotate + rotation) % 360 });
94
+ if (n === 1)
95
+ pageSize = `${Math.round((viewport.width * 72) / PRINT_DPI)}pt ${Math.round((viewport.height * 72) / PRINT_DPI)}pt`;
96
+ const canvas = document.createElement('canvas');
97
+ canvas.width = Math.floor(viewport.width);
98
+ canvas.height = Math.floor(viewport.height);
99
+ await page.render({ canvas, viewport, intent: 'print' }).promise;
100
+ const blob = await new Promise((resolve) => canvas.toBlob(resolve, 'image/png'));
101
+ if (blob)
102
+ urls.push(URL.createObjectURL(blob));
103
+ canvas.width = canvas.height = 0;
104
+ page.cleanup();
105
+ onProgress(n, pdf.numPages);
106
+ }
107
+ document.body.append(frame);
108
+ const doc = frame.contentDocument;
109
+ doc.open();
110
+ doc.write(`<!doctype html><html><head><style>@page{size:${pageSize};margin:0}html,body{margin:0}` +
111
+ `img{display:block;width:100%;height:auto;break-after:page}img:last-child{break-after:auto}</style></head><body></body></html>`);
112
+ doc.close();
113
+ await Promise.all(urls.map((src) => {
114
+ const img = doc.createElement('img');
115
+ img.src = src;
116
+ img.alt = '';
117
+ doc.body.append(img);
118
+ return img.decode().catch(() => undefined);
119
+ }));
120
+ const win = frame.contentWindow;
121
+ await new Promise((resolve) => {
122
+ win.addEventListener('afterprint', () => resolve(), { once: true });
123
+ win.focus();
124
+ win.print();
125
+ // Red de seguridad por si el navegador no emite afterprint.
126
+ setTimeout(resolve, 60_000);
127
+ });
128
+ }
129
+ finally {
130
+ frame.remove();
131
+ for (const url of urls)
132
+ URL.revokeObjectURL(url);
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Índice de búsqueda de una página. Ignora mayúsculas, tildes y ligaduras
138
+ * ("dirección" encuentra "direccion") y une los espacios seguidos. Solo cuenta
139
+ * los elementos con texto, en el mismo orden que la capa de texto de pdf.js.
140
+ */
141
+ function buildTextIndex(items) {
142
+ let text = '';
143
+ const map = [];
144
+ const push = (char, item, offset) => {
145
+ if (/\s/.test(char)) {
146
+ if (!text || text.endsWith(' '))
147
+ return;
148
+ char = ' ';
149
+ }
150
+ text += char;
151
+ map.push({ item, offset });
152
+ };
153
+ let index = 0;
154
+ for (const it of items) {
155
+ if (it.str === undefined)
156
+ continue;
157
+ const str = it.str;
158
+ for (let offset = 0; offset < str.length; offset++) {
159
+ for (const char of normalizeChar(str[offset]))
160
+ push(char, index, offset);
161
+ }
162
+ if (it.hasEOL)
163
+ push(' ', index, str.length);
164
+ index++;
165
+ }
166
+ return { text, map };
167
+ }
168
+ /** Normaliza el texto buscado igual que el índice. */
169
+ function normalizeQuery(query) {
170
+ return Array.from(query, normalizeChar).join('').replace(/\s+/g, ' ').trim();
171
+ }
172
+ /** Coincidencias de `query` en una página. */
173
+ function findMatches(index, query, page) {
174
+ const q = normalizeQuery(query);
175
+ if (!q)
176
+ return [];
177
+ const matches = [];
178
+ let from = index.text.indexOf(q);
179
+ while (from !== -1) {
180
+ const last = index.map[from + q.length - 1];
181
+ matches.push({ page, start: index.map[from], end: { item: last.item, offset: last.offset + 1 } });
182
+ from = index.text.indexOf(q, from + q.length);
183
+ }
184
+ return matches;
185
+ }
186
+ /**
187
+ * Resalta las coincidencias en la capa de texto de una página. Restaura antes
188
+ * el texto original. Devuelve el resaltado de `selected`, si está en la página.
189
+ */
190
+ function highlightMatches(divs, strings, matches, selected) {
191
+ for (let i = 0; i < divs.length; i++) {
192
+ if (divs[i].childElementCount)
193
+ divs[i].textContent = strings[i] ?? '';
194
+ }
195
+ const segments = new Map();
196
+ for (const m of matches) {
197
+ for (let item = m.start.item; item <= m.end.item; item++) {
198
+ const from = item === m.start.item ? m.start.offset : 0;
199
+ const to = item === m.end.item ? m.end.offset : (strings[item]?.length ?? 0);
200
+ if (to <= from)
201
+ continue;
202
+ const list = segments.get(item) ?? [];
203
+ list.push({ from, to, selected: m === selected });
204
+ segments.set(item, list);
205
+ }
206
+ }
207
+ let selectedEl = null;
208
+ for (const [item, list] of segments) {
209
+ const div = divs[item];
210
+ const str = strings[item];
211
+ if (!div || str === undefined)
212
+ continue;
213
+ const doc = div.ownerDocument;
214
+ const nodes = [];
215
+ let pos = 0;
216
+ for (const s of list.sort((a, b) => a.from - b.from)) {
217
+ if (s.from > pos)
218
+ nodes.push(doc.createTextNode(str.slice(pos, s.from)));
219
+ const mark = doc.createElement('span');
220
+ mark.className = s.selected ? 'cb-pdf__hl cb-is-selected' : 'cb-pdf__hl';
221
+ mark.textContent = str.slice(Math.max(pos, s.from), s.to);
222
+ nodes.push(mark);
223
+ if (s.selected)
224
+ selectedEl ??= mark;
225
+ pos = Math.max(pos, s.to);
226
+ }
227
+ if (pos < str.length)
228
+ nodes.push(doc.createTextNode(str.slice(pos)));
229
+ div.replaceChildren(...nodes);
230
+ }
231
+ return selectedEl;
232
+ }
233
+ function normalizeChar(char) {
234
+ return char.normalize('NFKD').replace(/\p{M}/gu, '').toLocaleLowerCase();
235
+ }
236
+
237
+ let nextUid = 0;
238
+ /** Puntos PDF (1/72") a píxeles CSS (1/96"): 100 % es el tamaño real. */
239
+ const CSS_UNITS = 96 / 72;
240
+ const ZOOM_LEVELS = [0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4];
241
+ const MIN_ZOOM = 0.25;
242
+ const MAX_ZOOM = 5;
243
+ /** Margen alrededor de las páginas (px), igual que en el CSS. */
244
+ const PAGE_GAP = 16;
245
+ /** Píxeles máximos de un canvas (evita agotar la memoria en zoom alto). */
246
+ const MAX_CANVAS_PIXELS = 2 ** 24;
247
+ const THUMB_WIDTH = 112;
248
+ const PDF_ICONS = {
249
+ 'zoom-in': zoomIn,
250
+ 'zoom-out': zoomOut,
251
+ printer,
252
+ 'panel-left': panelLeft,
253
+ };
254
+ /**
255
+ * Visor de PDF sobre [pdf.js](https://mozilla.github.io/pdf.js/) (`pdfjs-dist`,
256
+ * dependencia opcional que se carga al abrir el primer documento).
257
+ *
258
+ * Renderiza las páginas a medida que se ven, con texto seleccionable y
259
+ * legible por lectores de pantalla, búsqueda (ignora tildes y mayúsculas),
260
+ * miniaturas, zoom, giro, descarga, impresión y documentos con contraseña.
261
+ *
262
+ * @example
263
+ * ```html
264
+ * <cb-pdf-viewer src="/docs/extracto.pdf" [(page)]="pagina" zoom="page-width" />
265
+ * ```
266
+ */
267
+ class CbPdfViewer {
268
+ constructor() {
269
+ /** Textos de interfaz en el idioma activo (`provideCaribeNG({ locale })`). */
270
+ this.t = injectCbTranslation();
271
+ this.format = cbFormat;
272
+ /** Documento: URL, `ArrayBuffer`, `Uint8Array`, `Blob` o `File`. */
273
+ this.src = input(undefined, /* @ts-ignore */
274
+ ...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
275
+ /** Nombre del archivo al descargar (por defecto, el de la URL o del `File`). */
276
+ this.fileName = input(undefined, /* @ts-ignore */
277
+ ...(ngDevMode ? [{ debugName: "fileName" }] : /* istanbul ignore next */ []));
278
+ /** Contraseña para abrir el documento sin pedirla. */
279
+ this.password = input(undefined, /* @ts-ignore */
280
+ ...(ngDevMode ? [{ debugName: "password" }] : /* istanbul ignore next */ []));
281
+ /** Envía cookies en peticiones a otro origen. */
282
+ this.withCredentials = input(false, { ...(ngDevMode ? { debugName: "withCredentials" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
283
+ /** Cabeceras HTTP para descargar el documento (p. ej. `Authorization`). */
284
+ this.httpHeaders = input(undefined, /* @ts-ignore */
285
+ ...(ngDevMode ? [{ debugName: "httpHeaders" }] : /* istanbul ignore next */ []));
286
+ /** Página actual (desde 1). */
287
+ this.page = model(1, /* @ts-ignore */
288
+ ...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
289
+ this.zoom = model('page-width', /* @ts-ignore */
290
+ ...(ngDevMode ? [{ debugName: "zoom" }] : /* istanbul ignore next */ []));
291
+ this.rotation = model(0, /* @ts-ignore */
292
+ ...(ngDevMode ? [{ debugName: "rotation" }] : /* istanbul ignore next */ []));
293
+ /** Panel de miniaturas abierto. */
294
+ this.sidebar = model(false, /* @ts-ignore */
295
+ ...(ngDevMode ? [{ debugName: "sidebar" }] : /* istanbul ignore next */ []));
296
+ this.showToolbar = input(true, { ...(ngDevMode ? { debugName: "showToolbar" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
297
+ this.showSearch = input(true, { ...(ngDevMode ? { debugName: "showSearch" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
298
+ this.showThumbnails = input(true, { ...(ngDevMode ? { debugName: "showThumbnails" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
299
+ this.showRotate = input(true, { ...(ngDevMode ? { debugName: "showRotate" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
300
+ this.downloadable = input(true, { ...(ngDevMode ? { debugName: "downloadable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
301
+ this.printable = input(true, { ...(ngDevMode ? { debugName: "printable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
302
+ /** Nombre accesible del visor (por defecto, "Visor de PDF"). */
303
+ this.ariaLabel = input('', /* @ts-ignore */
304
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
305
+ /** Documento abierto. */
306
+ this.loaded = output();
307
+ /** No se pudo abrir el documento. */
308
+ this.loadError = output();
309
+ this.engine = inject(CbPdfEngine);
310
+ this.document = inject(DOCUMENT);
311
+ this.injector = inject(Injector);
312
+ this.isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
313
+ this.uid = `cb-pdf-${++nextUid}`;
314
+ this.state = signal('idle', /* @ts-ignore */
315
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
316
+ this.progress = signal(null, /* @ts-ignore */
317
+ ...(ngDevMode ? [{ debugName: "progress" }] : /* istanbul ignore next */ []));
318
+ this.passwordIncorrect = signal(false, /* @ts-ignore */
319
+ ...(ngDevMode ? [{ debugName: "passwordIncorrect" }] : /* istanbul ignore next */ []));
320
+ this.printProgress = signal(null, /* @ts-ignore */
321
+ ...(ngDevMode ? [{ debugName: "printProgress" }] : /* istanbul ignore next */ []));
322
+ this.numPages = signal(0, /* @ts-ignore */
323
+ ...(ngDevMode ? [{ debugName: "numPages" }] : /* istanbul ignore next */ []));
324
+ this.sizes = signal([], /* @ts-ignore */
325
+ ...(ngDevMode ? [{ debugName: "sizes" }] : /* istanbul ignore next */ []));
326
+ this.box = signal({ width: 0, height: 0 }, /* @ts-ignore */
327
+ ...(ngDevMode ? [{ debugName: "box" }] : /* istanbul ignore next */ []));
328
+ this.query = signal('', /* @ts-ignore */
329
+ ...(ngDevMode ? [{ debugName: "query" }] : /* istanbul ignore next */ []));
330
+ this.matches = signal([], /* @ts-ignore */
331
+ ...(ngDevMode ? [{ debugName: "matches" }] : /* istanbul ignore next */ []));
332
+ this.matchIndex = signal(-1, /* @ts-ignore */
333
+ ...(ngDevMode ? [{ debugName: "matchIndex" }] : /* istanbul ignore next */ []));
334
+ this.searching = signal(false, /* @ts-ignore */
335
+ ...(ngDevMode ? [{ debugName: "searching" }] : /* istanbul ignore next */ []));
336
+ this.zoomLevels = ZOOM_LEVELS;
337
+ this.viewport = viewChild('viewport', /* @ts-ignore */
338
+ ...(ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []));
339
+ this.pageEls = viewChildren('pageEl', /* @ts-ignore */
340
+ ...(ngDevMode ? [{ debugName: "pageEls" }] : /* istanbul ignore next */ []));
341
+ this.thumbEls = viewChildren('thumbEl', /* @ts-ignore */
342
+ ...(ngDevMode ? [{ debugName: "thumbEls" }] : /* istanbul ignore next */ []));
343
+ this.searchInput = viewChild('searchInput', /* @ts-ignore */
344
+ ...(ngDevMode ? [{ debugName: "searchInput" }] : /* istanbul ignore next */ []));
345
+ this.passwordInput = viewChild('passwordInput', /* @ts-ignore */
346
+ ...(ngDevMode ? [{ debugName: "passwordInput" }] : /* istanbul ignore next */ []));
347
+ /** Factor de zoom efectivo (1 = 100 %). */
348
+ this.scale = computed(() => {
349
+ const zoom = this.zoom();
350
+ if (typeof zoom === 'number')
351
+ return clamp(zoom, MIN_ZOOM, MAX_ZOOM);
352
+ const size = this.sizes()[this.page() - 1] ?? this.sizes()[0];
353
+ const { width, height } = this.box();
354
+ if (!size || width <= 0)
355
+ return 1;
356
+ const [w, h] = this.rotatedPoints(size);
357
+ const fitWidth = (width - PAGE_GAP * 2) / (w * CSS_UNITS);
358
+ const fit = zoom === 'page-fit' && height > 0 ? Math.min(fitWidth, (height - PAGE_GAP * 2) / (h * CSS_UNITS)) : fitWidth;
359
+ return clamp(fit, MIN_ZOOM, MAX_ZOOM);
360
+ }, /* @ts-ignore */
361
+ ...(ngDevMode ? [{ debugName: "scale" }] : /* istanbul ignore next */ []));
362
+ /** Tamaño de cada página en píxeles CSS con el zoom y el giro actuales. */
363
+ this.pageBoxes = computed(() => {
364
+ const factor = this.scale() * CSS_UNITS;
365
+ return this.sizes().map((size) => {
366
+ const [w, h] = this.rotatedPoints(size);
367
+ return { width: Math.floor(w * factor), height: Math.floor(h * factor), scaleFactor: factor, userUnit: size.userUnit };
368
+ });
369
+ }, /* @ts-ignore */
370
+ ...(ngDevMode ? [{ debugName: "pageBoxes" }] : /* istanbul ignore next */ []));
371
+ this.thumbBoxes = computed(() => this.sizes().map((size) => {
372
+ const [w, h] = this.rotatedPoints(size);
373
+ return { width: THUMB_WIDTH, height: Math.round((THUMB_WIDTH * h) / w) };
374
+ }), /* @ts-ignore */
375
+ ...(ngDevMode ? [{ debugName: "thumbBoxes" }] : /* istanbul ignore next */ []));
376
+ /** Valor del selector de zoom. */
377
+ this.zoomValue = computed(() => {
378
+ const zoom = this.zoom();
379
+ return typeof zoom === 'number' ? String(zoom) : zoom;
380
+ }, /* @ts-ignore */
381
+ ...(ngDevMode ? [{ debugName: "zoomValue" }] : /* istanbul ignore next */ []));
382
+ this.customZoom = computed(() => {
383
+ const zoom = this.zoom();
384
+ return typeof zoom === 'number' && !ZOOM_LEVELS.includes(zoom) ? zoom : null;
385
+ }, /* @ts-ignore */
386
+ ...(ngDevMode ? [{ debugName: "customZoom" }] : /* istanbul ignore next */ []));
387
+ this.searchStatus = computed(() => {
388
+ if (!this.query().trim())
389
+ return '';
390
+ const total = this.matches().length;
391
+ if (total)
392
+ return cbFormat(this.t().matchOf, this.matchIndex() + 1, total);
393
+ return this.searching() ? this.t().searching : this.t().noResults;
394
+ }, /* @ts-ignore */
395
+ ...(ngDevMode ? [{ debugName: "searchStatus" }] : /* istanbul ignore next */ []));
396
+ this.loadToken = 0;
397
+ this.searchToken = 0;
398
+ this.pages = new Map();
399
+ this.texts = new Map();
400
+ this.indexes = new Map();
401
+ this.rendered = new Map();
402
+ this.thumbs = new Map();
403
+ this.visible = new Set();
404
+ this.visibleThumbs = new Set();
405
+ /** Página que se ve al desplazarse (distingue el scroll del usuario de un cambio de `page`). */
406
+ this.visiblePage = 1;
407
+ this.scrollFrame = 0;
408
+ this.scrollToMatch = false;
409
+ const registry = inject(CbIconRegistry);
410
+ for (const [name, body] of Object.entries(PDF_ICONS)) {
411
+ if (!registry.has(name))
412
+ registry.register({ [name]: body });
413
+ }
414
+ effect(() => {
415
+ const src = this.src();
416
+ const password = this.password();
417
+ untracked(() => void this.open(src, password));
418
+ });
419
+ // Al cambiar el zoom o el giro se conserva el punto de lectura.
420
+ effect(() => {
421
+ this.scale();
422
+ this.rotation();
423
+ if (untracked(this.state) !== 'ready')
424
+ return;
425
+ const anchor = this.readAnchor();
426
+ afterNextRender(() => {
427
+ if (anchor)
428
+ this.restoreAnchor(anchor);
429
+ this.renderVisible();
430
+ this.renderThumbs();
431
+ }, { injector: this.injector });
432
+ });
433
+ // Cambio de `page` desde fuera (o desde la barra): desplaza hasta ella.
434
+ effect(() => {
435
+ const page = this.page();
436
+ if (this.state() !== 'ready' || page === this.visiblePage)
437
+ return;
438
+ afterNextRender(() => this.scrollToPage(page), { injector: this.injector });
439
+ });
440
+ effect(() => {
441
+ this.pageEls();
442
+ if (this.state() === 'ready')
443
+ untracked(() => this.observePages());
444
+ });
445
+ effect(() => {
446
+ this.thumbEls();
447
+ if (this.sidebar() && this.state() === 'ready')
448
+ untracked(() => this.observeThumbs());
449
+ });
450
+ effect(() => {
451
+ this.matches();
452
+ this.matchIndex();
453
+ untracked(() => this.refreshHighlights());
454
+ });
455
+ inject(DestroyRef).onDestroy(() => {
456
+ this.loadToken++;
457
+ this.close();
458
+ this.resizeObserver?.disconnect();
459
+ clearTimeout(this.searchTimer);
460
+ if (this.scrollFrame)
461
+ cancelAnimationFrame(this.scrollFrame);
462
+ });
463
+ }
464
+ // --- API pública -----------------------------------------------------------
465
+ goToPage(page) {
466
+ const n = clamp(Math.round(page), 1, Math.max(1, this.numPages()));
467
+ this.page.set(n);
468
+ if (n === this.visiblePage)
469
+ this.scrollToPage(n);
470
+ }
471
+ previousPage() {
472
+ this.goToPage(this.page() - 1);
473
+ }
474
+ nextPage() {
475
+ this.goToPage(this.page() + 1);
476
+ }
477
+ zoomIn() {
478
+ const scale = this.scale();
479
+ this.zoom.set(ZOOM_LEVELS.find((z) => z > scale + 0.01) ?? Math.min(MAX_ZOOM, round2(scale * 1.25)));
480
+ }
481
+ zoomOut() {
482
+ const scale = this.scale();
483
+ this.zoom.set([...ZOOM_LEVELS].reverse().find((z) => z < scale - 0.01) ?? Math.max(MIN_ZOOM, round2(scale / 1.25)));
484
+ }
485
+ /** Gira 90° a la derecha (o a la izquierda con `-1`). */
486
+ rotate(direction = 1) {
487
+ this.rotation.set((((this.rotation() + direction * 90) % 360) + 360) % 360);
488
+ }
489
+ /** Busca en todo el documento; resalta y lleva a la primera coincidencia. */
490
+ async search(query) {
491
+ clearTimeout(this.searchTimer);
492
+ this.query.set(query);
493
+ const token = ++this.searchToken;
494
+ this.matches.set([]);
495
+ this.matchIndex.set(-1);
496
+ if (!query.trim() || !this.pdf) {
497
+ this.searching.set(false);
498
+ return;
499
+ }
500
+ this.searching.set(true);
501
+ const found = [];
502
+ // La primera coincidencia se busca desde la página actual.
503
+ const start = this.page();
504
+ for (let n = 1; n <= this.numPages(); n++) {
505
+ const index = await this.textIndex(n).catch(() => null);
506
+ if (token !== this.searchToken)
507
+ return;
508
+ if (!index)
509
+ continue;
510
+ const pageMatches = findMatches(index, query, n);
511
+ if (!pageMatches.length)
512
+ continue;
513
+ found.push(...pageMatches);
514
+ this.matches.set([...found]);
515
+ const current = this.matchIndex();
516
+ const first = found.findIndex((m) => m.page >= start);
517
+ if (current === -1 && first !== -1)
518
+ this.selectMatch(first);
519
+ }
520
+ if (token !== this.searchToken)
521
+ return;
522
+ this.searching.set(false);
523
+ if (this.matchIndex() === -1 && found.length)
524
+ this.selectMatch(0);
525
+ }
526
+ findNext() {
527
+ const total = this.matches().length;
528
+ if (total)
529
+ this.selectMatch((this.matchIndex() + 1) % total);
530
+ }
531
+ findPrevious() {
532
+ const total = this.matches().length;
533
+ if (total)
534
+ this.selectMatch((this.matchIndex() - 1 + total) % total);
535
+ }
536
+ /** Descarga el documento original. */
537
+ async download() {
538
+ if (!this.pdf)
539
+ return;
540
+ const src = this.src();
541
+ const blob = src instanceof Blob ? src : new Blob([(await this.pdf.getData())], { type: 'application/pdf' });
542
+ const url = URL.createObjectURL(blob);
543
+ const a = this.document.createElement('a');
544
+ a.href = url;
545
+ a.download = this.downloadName();
546
+ a.style.display = 'none';
547
+ this.document.body.append(a);
548
+ a.click();
549
+ a.remove();
550
+ setTimeout(() => URL.revokeObjectURL(url), 0);
551
+ }
552
+ /** Imprime el documento (no la página de la aplicación). */
553
+ async print() {
554
+ if (!this.pdf || this.printProgress())
555
+ return;
556
+ this.printProgress.set({ done: 0, total: this.numPages() });
557
+ try {
558
+ await printPdfDocument(this.pdf, this.document, (done, total) => this.printProgress.set({ done, total }), this.rotation());
559
+ }
560
+ finally {
561
+ this.printProgress.set(null);
562
+ }
563
+ }
564
+ // --- Carga -----------------------------------------------------------------
565
+ async open(src, password) {
566
+ const token = ++this.loadToken;
567
+ this.close();
568
+ if (!src || !this.isBrowser) {
569
+ this.state.set('idle');
570
+ return;
571
+ }
572
+ this.state.set('loading');
573
+ this.progress.set(null);
574
+ this.passwordIncorrect.set(false);
575
+ try {
576
+ const pdfjs = await this.engine.load();
577
+ const data = await toData(src);
578
+ if (token !== this.loadToken)
579
+ return;
580
+ this.pdfjs = pdfjs;
581
+ const task = pdfjs.getDocument({
582
+ ...this.engine.resources(),
583
+ ...(data ? { data } : { url: this.engine.resolve(src) }),
584
+ password,
585
+ withCredentials: this.withCredentials(),
586
+ httpHeaders: this.httpHeaders(),
587
+ });
588
+ this.loadingTask = task;
589
+ task.onProgress = ({ loaded, total }) => {
590
+ if (token === this.loadToken)
591
+ this.progress.set(total ? Math.min(100, Math.round((loaded / total) * 100)) : null);
592
+ };
593
+ task.onPassword = (update, reason) => {
594
+ if (token !== this.loadToken)
595
+ return;
596
+ this.passwordCallback = update;
597
+ this.passwordIncorrect.set(reason === (pdfjs.PasswordResponses?.INCORRECT_PASSWORD ?? 2));
598
+ this.state.set('password');
599
+ afterNextRender(() => this.passwordInput()?.nativeElement.focus(), { injector: this.injector });
600
+ };
601
+ const pdf = await task.promise;
602
+ if (token !== this.loadToken) {
603
+ void task.destroy().catch(() => undefined);
604
+ return;
605
+ }
606
+ this.pdf = pdf;
607
+ // Mide el área antes del primer render para que "ajustar al ancho" no renderice dos veces.
608
+ this.observeResize();
609
+ const first = sizeOf(await this.getPage(1));
610
+ if (token !== this.loadToken)
611
+ return;
612
+ this.sizes.set(Array.from({ length: pdf.numPages }, () => first));
613
+ this.numPages.set(pdf.numPages);
614
+ const page = clamp(this.page(), 1, pdf.numPages);
615
+ this.visiblePage = 1;
616
+ this.page.set(page);
617
+ this.state.set('ready');
618
+ this.loaded.emit({ numPages: pdf.numPages });
619
+ afterNextRender(() => {
620
+ if (page > 1)
621
+ this.scrollToPage(page);
622
+ this.renderVisible();
623
+ }, { injector: this.injector });
624
+ void this.measurePages(token);
625
+ }
626
+ catch (error) {
627
+ if (token !== this.loadToken)
628
+ return;
629
+ this.state.set('error');
630
+ if (isDevMode())
631
+ console.error('[cb-pdf-viewer]', error);
632
+ this.loadError.emit(error);
633
+ }
634
+ }
635
+ submitPassword(event, value) {
636
+ event.preventDefault();
637
+ if (!this.passwordCallback)
638
+ return;
639
+ this.state.set('loading');
640
+ this.passwordCallback(value);
641
+ }
642
+ /** Lee el tamaño real de todas las páginas (pueden ser distintas de la primera). */
643
+ async measurePages(token) {
644
+ const sizes = [...this.sizes()];
645
+ let changed = false;
646
+ for (let n = 2; n <= sizes.length; n++) {
647
+ const page = await this.getPage(n).catch(() => null);
648
+ if (token !== this.loadToken)
649
+ return;
650
+ if (!page)
651
+ continue;
652
+ const size = sizeOf(page);
653
+ const prev = sizes[n - 1];
654
+ if (size.width !== prev.width || size.height !== prev.height || size.rotate !== prev.rotate || size.userUnit !== prev.userUnit) {
655
+ sizes[n - 1] = size;
656
+ changed = true;
657
+ }
658
+ }
659
+ if (changed)
660
+ this.sizes.set(sizes);
661
+ }
662
+ close() {
663
+ for (const page of [...this.rendered.keys()])
664
+ this.unrender(page);
665
+ this.pageObserver?.disconnect();
666
+ this.thumbObserver?.disconnect();
667
+ this.pageObserver = this.thumbObserver = undefined;
668
+ void this.loadingTask?.destroy().catch(() => undefined);
669
+ this.loadingTask = undefined;
670
+ this.pdf = undefined;
671
+ this.passwordCallback = undefined;
672
+ this.pages.clear();
673
+ this.texts.clear();
674
+ this.indexes.clear();
675
+ this.thumbs.clear();
676
+ this.visible.clear();
677
+ this.visibleThumbs.clear();
678
+ this.searchToken++;
679
+ this.matches.set([]);
680
+ this.matchIndex.set(-1);
681
+ this.searching.set(false);
682
+ this.numPages.set(0);
683
+ this.sizes.set([]);
684
+ }
685
+ getPage(n) {
686
+ let page = this.pages.get(n);
687
+ if (!page) {
688
+ page = this.pdf.getPage(n);
689
+ this.pages.set(n, page);
690
+ }
691
+ return page;
692
+ }
693
+ textContent(n) {
694
+ let text = this.texts.get(n);
695
+ if (!text) {
696
+ text = this.getPage(n).then((page) => page.getTextContent());
697
+ this.texts.set(n, text);
698
+ }
699
+ return text;
700
+ }
701
+ textIndex(n) {
702
+ let index = this.indexes.get(n);
703
+ if (!index) {
704
+ index = this.textContent(n).then((text) => buildTextIndex(text.items));
705
+ this.indexes.set(n, index);
706
+ }
707
+ return index;
708
+ }
709
+ // --- Renderizado ------------------------------------------------------------
710
+ /** Observa qué páginas están cerca de la vista para renderizar solo esas. */
711
+ observePages() {
712
+ const root = this.viewport()?.nativeElement;
713
+ if (!root)
714
+ return;
715
+ this.pageObserver?.disconnect();
716
+ if (typeof IntersectionObserver === 'undefined') {
717
+ this.visible.clear();
718
+ for (let n = 1; n <= this.numPages(); n++)
719
+ this.visible.add(n);
720
+ this.renderVisible();
721
+ return;
722
+ }
723
+ this.pageObserver = new IntersectionObserver((entries) => {
724
+ for (const entry of entries) {
725
+ const n = Number(entry.target.dataset['page']);
726
+ if (entry.isIntersecting)
727
+ this.visible.add(n);
728
+ else {
729
+ this.visible.delete(n);
730
+ this.unrender(n);
731
+ }
732
+ }
733
+ this.renderVisible();
734
+ }, { root, rootMargin: '50% 0px' });
735
+ for (const el of this.pageEls())
736
+ this.pageObserver.observe(el.nativeElement);
737
+ }
738
+ renderVisible() {
739
+ const current = this.page();
740
+ const pages = [...this.visible].sort((a, b) => Math.abs(a - current) - Math.abs(b - current));
741
+ for (const n of pages)
742
+ void this.renderPage(n);
743
+ }
744
+ async renderPage(n) {
745
+ const scale = this.scale() * CSS_UNITS;
746
+ const rotation = this.rotation();
747
+ const key = `${scale}:${rotation}`;
748
+ if (this.rendered.get(n)?.key === key || !this.pdf)
749
+ return;
750
+ this.unrender(n);
751
+ let cancelled = false;
752
+ let renderTask;
753
+ const entry = {
754
+ key,
755
+ cancel: () => {
756
+ cancelled = true;
757
+ renderTask?.cancel();
758
+ entry.textLayer?.cancel();
759
+ },
760
+ };
761
+ this.rendered.set(n, entry);
762
+ try {
763
+ const page = await this.getPage(n);
764
+ const el = this.pageEl(n);
765
+ if (cancelled || !el)
766
+ return;
767
+ const viewport = page.getViewport({ scale, rotation: (page.rotate + rotation) % 360 });
768
+ const canvas = this.document.createElement('canvas');
769
+ const ratio = outputScale(viewport.width, viewport.height);
770
+ canvas.width = Math.floor(viewport.width * ratio);
771
+ canvas.height = Math.floor(viewport.height * ratio);
772
+ canvas.setAttribute('aria-hidden', 'true');
773
+ const task = page.render({ canvas, viewport, transform: ratio === 1 ? undefined : [ratio, 0, 0, ratio, 0, 0] });
774
+ renderTask = task;
775
+ await task.promise;
776
+ if (cancelled)
777
+ return;
778
+ el.querySelector('.cb-pdf__canvas')?.replaceChildren(canvas);
779
+ const textEl = el.querySelector('.cb-pdf__text');
780
+ if (!textEl || !this.pdfjs?.TextLayer)
781
+ return;
782
+ const textContentSource = await this.textContent(n);
783
+ if (cancelled)
784
+ return;
785
+ textEl.replaceChildren();
786
+ const textLayer = new this.pdfjs.TextLayer({ textContentSource, container: textEl, viewport });
787
+ entry.textLayer = textLayer;
788
+ await textLayer.render();
789
+ if (cancelled)
790
+ return;
791
+ this.highlightPage(n, entry);
792
+ }
793
+ catch {
794
+ // Render cancelado o página dañada: se deja en blanco.
795
+ if (this.rendered.get(n) === entry && !cancelled)
796
+ entry.key = '';
797
+ }
798
+ }
799
+ unrender(n) {
800
+ const entry = this.rendered.get(n);
801
+ if (!entry)
802
+ return;
803
+ entry.cancel();
804
+ this.rendered.delete(n);
805
+ const el = this.pageEl(n);
806
+ el?.querySelector('.cb-pdf__canvas')?.replaceChildren();
807
+ el?.querySelector('.cb-pdf__text')?.replaceChildren();
808
+ }
809
+ observeThumbs() {
810
+ const root = this.document.getElementById(`${this.uid}-thumbs`);
811
+ if (!root)
812
+ return;
813
+ this.thumbObserver?.disconnect();
814
+ if (typeof IntersectionObserver === 'undefined') {
815
+ for (let n = 1; n <= this.numPages(); n++)
816
+ this.visibleThumbs.add(n);
817
+ this.renderThumbs();
818
+ return;
819
+ }
820
+ this.thumbObserver = new IntersectionObserver((entries) => {
821
+ for (const entry of entries) {
822
+ const n = Number(entry.target.dataset['page']);
823
+ if (entry.isIntersecting)
824
+ this.visibleThumbs.add(n);
825
+ else
826
+ this.visibleThumbs.delete(n);
827
+ }
828
+ this.renderThumbs();
829
+ }, { root, rootMargin: '100% 0px' });
830
+ for (const el of this.thumbEls())
831
+ this.thumbObserver.observe(el.nativeElement);
832
+ }
833
+ renderThumbs() {
834
+ if (!this.sidebar())
835
+ return;
836
+ for (const n of this.visibleThumbs)
837
+ void this.renderThumb(n);
838
+ }
839
+ async renderThumb(n) {
840
+ const key = String(this.rotation());
841
+ if (this.thumbs.get(n) === key || !this.pdf)
842
+ return;
843
+ this.thumbs.set(n, key);
844
+ try {
845
+ const page = await this.getPage(n);
846
+ const rotation = (page.rotate + this.rotation()) % 360;
847
+ const base = page.getViewport({ scale: 1, rotation });
848
+ const ratio = Math.min(2, globalThis.devicePixelRatio || 1);
849
+ const viewport = page.getViewport({ scale: (THUMB_WIDTH / base.width) * ratio, rotation });
850
+ const canvas = this.document.createElement('canvas');
851
+ canvas.width = Math.floor(viewport.width);
852
+ canvas.height = Math.floor(viewport.height);
853
+ canvas.setAttribute('aria-hidden', 'true');
854
+ await page.render({ canvas, viewport }).promise;
855
+ if (this.thumbs.get(n) !== key)
856
+ return;
857
+ this.thumbEls()
858
+ .find((el) => el.nativeElement.dataset['page'] === String(n))
859
+ ?.nativeElement.querySelector('.cb-pdf__thumb-canvas')
860
+ ?.replaceChildren(canvas);
861
+ }
862
+ catch {
863
+ this.thumbs.delete(n);
864
+ }
865
+ }
866
+ // --- Búsqueda ---------------------------------------------------------------
867
+ selectMatch(index) {
868
+ this.matchIndex.set(index);
869
+ const match = this.matches()[index];
870
+ if (!match)
871
+ return;
872
+ this.scrollToMatch = true;
873
+ if (this.rendered.get(match.page)?.textLayer)
874
+ this.refreshHighlights();
875
+ else
876
+ this.goToPage(match.page);
877
+ }
878
+ refreshHighlights() {
879
+ for (const [n, entry] of this.rendered)
880
+ this.highlightPage(n, entry);
881
+ }
882
+ highlightPage(n, entry) {
883
+ const layer = entry.textLayer;
884
+ if (!layer)
885
+ return;
886
+ const matches = this.matches();
887
+ const selected = matches[this.matchIndex()] ?? null;
888
+ const el = highlightMatches(layer.textDivs, layer.textContentItemsStr, matches.filter((m) => m.page === n), selected?.page === n ? selected : null);
889
+ if (el && this.scrollToMatch) {
890
+ this.scrollToMatch = false;
891
+ el.scrollIntoView?.({ block: 'center', inline: 'nearest' });
892
+ }
893
+ }
894
+ onSearchInput(value) {
895
+ this.query.set(value);
896
+ clearTimeout(this.searchTimer);
897
+ this.searchTimer = setTimeout(() => void this.search(value), 250);
898
+ }
899
+ onSearchKeydown(event, value) {
900
+ if (event.key === 'Enter') {
901
+ event.preventDefault();
902
+ if (value !== this.query() || (!this.matches().length && !this.searching()))
903
+ void this.search(value);
904
+ else if (event.shiftKey)
905
+ this.findPrevious();
906
+ else
907
+ this.findNext();
908
+ }
909
+ else if (event.key === 'Escape' && value) {
910
+ event.preventDefault();
911
+ event.stopPropagation();
912
+ event.target.value = '';
913
+ void this.search('');
914
+ }
915
+ }
916
+ // --- Desplazamiento y tamaño ---------------------------------------------------
917
+ onScroll() {
918
+ cancelAnimationFrame(this.scrollFrame);
919
+ this.scrollFrame = requestAnimationFrame(() => {
920
+ const page = this.mostVisiblePage();
921
+ if (page && page !== this.visiblePage) {
922
+ this.visiblePage = page;
923
+ this.page.set(page);
924
+ }
925
+ });
926
+ }
927
+ mostVisiblePage() {
928
+ const root = this.viewport()?.nativeElement;
929
+ if (!root)
930
+ return null;
931
+ const top = root.scrollTop;
932
+ const bottom = top + root.clientHeight;
933
+ let best = null;
934
+ let bestArea = 0;
935
+ for (const { nativeElement: el } of this.pageEls()) {
936
+ const visible = Math.min(bottom, el.offsetTop + el.offsetHeight) - Math.max(top, el.offsetTop);
937
+ if (visible > bestArea) {
938
+ bestArea = visible;
939
+ best = Number(el.dataset['page']);
940
+ }
941
+ }
942
+ return best;
943
+ }
944
+ scrollToPage(n) {
945
+ const root = this.viewport()?.nativeElement;
946
+ const el = this.pageEl(n);
947
+ if (!root || !el)
948
+ return;
949
+ this.visiblePage = n;
950
+ root.scrollTop = el.offsetTop - PAGE_GAP;
951
+ }
952
+ readAnchor() {
953
+ const root = this.viewport()?.nativeElement;
954
+ const el = this.pageEl(this.visiblePage);
955
+ if (!root || !el || !el.offsetHeight)
956
+ return null;
957
+ return { page: this.visiblePage, ratio: (root.scrollTop - el.offsetTop) / el.offsetHeight };
958
+ }
959
+ restoreAnchor(anchor) {
960
+ const root = this.viewport()?.nativeElement;
961
+ const el = this.pageEl(anchor.page);
962
+ if (root && el)
963
+ root.scrollTop = el.offsetTop + anchor.ratio * el.offsetHeight;
964
+ }
965
+ observeResize() {
966
+ const root = this.viewport()?.nativeElement;
967
+ if (!root)
968
+ return;
969
+ const measure = () => this.box.set({ width: root.clientWidth, height: root.clientHeight });
970
+ measure();
971
+ if (typeof ResizeObserver === 'undefined' || this.resizeObserver)
972
+ return;
973
+ this.resizeObserver = new ResizeObserver(measure);
974
+ this.resizeObserver.observe(root);
975
+ }
976
+ pageEl(n) {
977
+ return this.pageEls()[n - 1]?.nativeElement;
978
+ }
979
+ // --- Barra y teclado -------------------------------------------------------------
980
+ onPageInput(event) {
981
+ const input = event.target;
982
+ const value = Number.parseInt(input.value, 10);
983
+ if (Number.isFinite(value))
984
+ this.goToPage(value);
985
+ input.value = String(this.page());
986
+ }
987
+ onZoomSelect(value) {
988
+ this.zoom.set(value === 'page-width' || value === 'page-fit' ? value : Number(value));
989
+ }
990
+ onThumbKeydown(event, n) {
991
+ const next = event.key === 'ArrowDown' ? n + 1 : event.key === 'ArrowUp' ? n - 1 : event.key === 'Home' ? 1 : event.key === 'End' ? this.numPages() : 0;
992
+ if (!next || next < 1 || next > this.numPages())
993
+ return;
994
+ event.preventDefault();
995
+ this.thumbEls()[next - 1]?.nativeElement.focus();
996
+ }
997
+ onHostKeydown(event) {
998
+ if (this.state() !== 'ready' || !(event.ctrlKey || event.metaKey) || event.altKey)
999
+ return;
1000
+ const key = event.key.toLowerCase();
1001
+ if (key === '+' || key === '=')
1002
+ this.zoomIn();
1003
+ else if (key === '-')
1004
+ this.zoomOut();
1005
+ else if (key === '0')
1006
+ this.zoom.set('page-width');
1007
+ else if (key === 'f' && this.showToolbar() && this.showSearch()) {
1008
+ const input = this.searchInput()?.nativeElement;
1009
+ input?.focus();
1010
+ input?.select();
1011
+ }
1012
+ else if (key === 'p' && this.printable())
1013
+ void this.print();
1014
+ else
1015
+ return;
1016
+ event.preventDefault();
1017
+ }
1018
+ onWheel(event) {
1019
+ if (!(event.ctrlKey || event.metaKey) || this.state() !== 'ready')
1020
+ return;
1021
+ event.preventDefault();
1022
+ if (event.deltaY < 0)
1023
+ this.zoomIn();
1024
+ else
1025
+ this.zoomOut();
1026
+ }
1027
+ percent(zoom) {
1028
+ return `${Math.round(zoom * 100)} %`;
1029
+ }
1030
+ downloadName() {
1031
+ const name = this.fileName();
1032
+ if (name)
1033
+ return name;
1034
+ const src = this.src();
1035
+ if (typeof File !== 'undefined' && src instanceof File && src.name)
1036
+ return src.name;
1037
+ if (typeof src === 'string' || src instanceof URL) {
1038
+ const last = decodeURIComponent(String(src).split(/[?#]/)[0].split('/').pop() ?? '');
1039
+ if (last)
1040
+ return /\.pdf$/i.test(last) ? last : `${last}.pdf`;
1041
+ }
1042
+ return 'document.pdf';
1043
+ }
1044
+ rotatedPoints(size) {
1045
+ return (size.rotate + this.rotation()) % 180 === 0 ? [size.width, size.height] : [size.height, size.width];
1046
+ }
1047
+ 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: `
1049
+ @if (showToolbar()) {
1050
+ <div class="cb-pdf__toolbar">
1051
+ @if (showThumbnails()) {
1052
+ <div class="cb-pdf__group">
1053
+ <button
1054
+ type="button"
1055
+ class="cb-pdf__tool"
1056
+ [attr.aria-label]="t().thumbnails"
1057
+ [attr.aria-expanded]="sidebar()"
1058
+ [attr.aria-controls]="uid + '-thumbs'"
1059
+ [disabled]="state() !== 'ready'"
1060
+ (click)="sidebar.set(!sidebar())"
1061
+ >
1062
+ <cb-icon name="panel-left" [size]="18" aria-hidden="true" />
1063
+ </button>
1064
+ </div>
1065
+ }
1066
+
1067
+ <div class="cb-pdf__group" role="group" [attr.aria-label]="t().pagination">
1068
+ <button
1069
+ type="button"
1070
+ class="cb-pdf__tool"
1071
+ [attr.aria-label]="t().previousPage"
1072
+ [disabled]="state() !== 'ready' || page() <= 1"
1073
+ (click)="previousPage()"
1074
+ >
1075
+ <cb-icon name="chevron-up" [size]="18" aria-hidden="true" />
1076
+ </button>
1077
+ <button
1078
+ type="button"
1079
+ class="cb-pdf__tool"
1080
+ [attr.aria-label]="t().nextPage"
1081
+ [disabled]="state() !== 'ready' || page() >= numPages()"
1082
+ (click)="nextPage()"
1083
+ >
1084
+ <cb-icon name="chevron-down" [size]="18" aria-hidden="true" />
1085
+ </button>
1086
+ <input
1087
+ class="cb-pdf__field cb-pdf__page-input"
1088
+ type="text"
1089
+ inputmode="numeric"
1090
+ autocomplete="off"
1091
+ [id]="uid + '-page'"
1092
+ [attr.aria-label]="t().pageNumber"
1093
+ [attr.aria-describedby]="uid + '-total'"
1094
+ [value]="page()"
1095
+ [disabled]="state() !== 'ready'"
1096
+ (change)="onPageInput($event)"
1097
+ (keydown.enter)="onPageInput($event)"
1098
+ />
1099
+ <span class="cb-pdf__total" [id]="uid + '-total'">{{ format(t().ofTotal, numPages() || '–') }}</span>
1100
+ </div>
1101
+
1102
+ <div class="cb-pdf__group" role="group" [attr.aria-label]="t().zoomLevel">
1103
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().zoomOut" [disabled]="state() !== 'ready' || scale() <= 0.25" (click)="zoomOut()">
1104
+ <cb-icon name="zoom-out" [size]="18" aria-hidden="true" />
1105
+ </button>
1106
+ <select
1107
+ class="cb-pdf__field cb-pdf__zoom"
1108
+ [attr.aria-label]="t().zoomLevel"
1109
+ [disabled]="state() !== 'ready'"
1110
+ (change)="onZoomSelect($any($event.target).value)"
1111
+ >
1112
+ <option value="page-width" [selected]="zoomValue() === 'page-width'">{{ t().fitWidth }}</option>
1113
+ <option value="page-fit" [selected]="zoomValue() === 'page-fit'">{{ t().fitPage }}</option>
1114
+ @if (customZoom(); as custom) {
1115
+ <option [value]="custom" selected>{{ percent(custom) }}</option>
1116
+ }
1117
+ @for (level of zoomLevels; track level) {
1118
+ <option [value]="level" [selected]="zoomValue() === '' + level">{{ percent(level) }}</option>
1119
+ }
1120
+ </select>
1121
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().zoomIn" [disabled]="state() !== 'ready' || scale() >= 5" (click)="zoomIn()">
1122
+ <cb-icon name="zoom-in" [size]="18" aria-hidden="true" />
1123
+ </button>
1124
+ @if (showRotate()) {
1125
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().rotateRight" [disabled]="state() !== 'ready'" (click)="rotate()">
1126
+ <cb-icon name="rotate-cw" [size]="18" aria-hidden="true" />
1127
+ </button>
1128
+ }
1129
+ </div>
1130
+
1131
+ <span class="cb-pdf__spacer"></span>
1132
+
1133
+ @if (showSearch()) {
1134
+ <div class="cb-pdf__group cb-pdf__search" role="search">
1135
+ <cb-icon class="cb-pdf__search-icon" name="search" [size]="16" aria-hidden="true" />
1136
+ <input
1137
+ #searchInput
1138
+ class="cb-pdf__field cb-pdf__search-input"
1139
+ type="search"
1140
+ autocomplete="off"
1141
+ [attr.aria-label]="t().searchInDocument"
1142
+ [attr.aria-describedby]="uid + '-status'"
1143
+ [placeholder]="t().searchInDocument"
1144
+ [disabled]="state() !== 'ready'"
1145
+ (input)="onSearchInput(searchInput.value)"
1146
+ (keydown)="onSearchKeydown($event, searchInput.value)"
1147
+ />
1148
+ <span class="cb-pdf__status" [id]="uid + '-status'" aria-live="polite">{{ searchStatus() }}</span>
1149
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().previousMatch" [disabled]="!matches().length" (click)="findPrevious()">
1150
+ <cb-icon name="chevron-left" [size]="18" aria-hidden="true" />
1151
+ </button>
1152
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().nextMatch" [disabled]="!matches().length" (click)="findNext()">
1153
+ <cb-icon name="chevron-right" [size]="18" aria-hidden="true" />
1154
+ </button>
1155
+ </div>
1156
+ }
1157
+
1158
+ @if (downloadable() || printable()) {
1159
+ <div class="cb-pdf__group">
1160
+ @if (downloadable()) {
1161
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().download" [disabled]="state() !== 'ready'" (click)="download()">
1162
+ <cb-icon name="download" [size]="18" aria-hidden="true" />
1163
+ </button>
1164
+ }
1165
+ @if (printable()) {
1166
+ <button
1167
+ type="button"
1168
+ class="cb-pdf__tool"
1169
+ [attr.aria-label]="t().print"
1170
+ [disabled]="state() !== 'ready' || !!printProgress()"
1171
+ (click)="print()"
1172
+ >
1173
+ <cb-icon name="printer" [size]="18" aria-hidden="true" />
1174
+ </button>
1175
+ }
1176
+ </div>
1177
+ }
1178
+ </div>
1179
+ }
1180
+
1181
+ <div class="cb-pdf__body">
1182
+ @if (state() === 'ready' && sidebar() && showThumbnails()) {
1183
+ <nav class="cb-pdf__sidebar" [id]="uid + '-thumbs'" [attr.aria-label]="t().thumbnails">
1184
+ @for (thumb of thumbBoxes(); track $index; let i = $index) {
1185
+ <button
1186
+ #thumbEl
1187
+ type="button"
1188
+ class="cb-pdf__thumb"
1189
+ [class.cb-is-active]="page() === i + 1"
1190
+ [attr.data-page]="i + 1"
1191
+ [attr.aria-label]="format(t().pageOf, i + 1, numPages())"
1192
+ [attr.aria-current]="page() === i + 1 ? 'page' : null"
1193
+ (click)="goToPage(i + 1)"
1194
+ (keydown)="onThumbKeydown($event, i + 1)"
1195
+ >
1196
+ <span class="cb-pdf__thumb-canvas" [style.width.px]="thumb.width" [style.height.px]="thumb.height"></span>
1197
+ <span class="cb-pdf__thumb-label" aria-hidden="true">{{ i + 1 }}</span>
1198
+ </button>
1199
+ }
1200
+ </nav>
1201
+ }
1202
+
1203
+ <div
1204
+ #viewport
1205
+ class="cb-pdf__viewport"
1206
+ tabindex="0"
1207
+ role="document"
1208
+ [attr.aria-label]="t().pdfDocument"
1209
+ [attr.aria-busy]="state() === 'loading' ? 'true' : null"
1210
+ (scroll)="onScroll()"
1211
+ (wheel)="onWheel($event)"
1212
+ >
1213
+ @switch (state()) {
1214
+ @case ('loading') {
1215
+ <div class="cb-pdf__message" role="status">
1216
+ <cb-spinner [size]="32" aria-hidden="true" />
1217
+ <span>{{ t().loadingDocument }}@if (progress() !== null) { {{ progress() }} %}</span>
1218
+ </div>
1219
+ }
1220
+ @case ('error') {
1221
+ <div class="cb-pdf__message cb-pdf__message--error" role="alert">{{ t().documentError }}</div>
1222
+ }
1223
+ @case ('password') {
1224
+ <form class="cb-pdf__password" (submit)="submitPassword($event, passwordInput.value)">
1225
+ <cb-icon name="lock" [size]="28" aria-hidden="true" />
1226
+ <p class="cb-pdf__password-text" [id]="uid + '-pwd-text'">{{ t().protectedDocument }}</p>
1227
+ <label class="cb-pdf__password-label" [for]="uid + '-pwd'">{{ t().password }}</label>
1228
+ <div class="cb-pdf__password-row">
1229
+ <input
1230
+ #passwordInput
1231
+ class="cb-pdf__field"
1232
+ type="password"
1233
+ autocomplete="off"
1234
+ [id]="uid + '-pwd'"
1235
+ [attr.aria-describedby]="passwordIncorrect() ? uid + '-pwd-error' : uid + '-pwd-text'"
1236
+ [attr.aria-invalid]="passwordIncorrect() ? 'true' : null"
1237
+ />
1238
+ <button type="submit" class="cb-pdf__submit">{{ t().open }}</button>
1239
+ </div>
1240
+ @if (passwordIncorrect()) {
1241
+ <p class="cb-pdf__password-error" [id]="uid + '-pwd-error'" role="alert">{{ t().incorrectPassword }}</p>
1242
+ }
1243
+ </form>
1244
+ }
1245
+ @case ('ready') {
1246
+ <div class="cb-pdf__pages">
1247
+ @for (box of pageBoxes(); track $index; let i = $index) {
1248
+ <div
1249
+ #pageEl
1250
+ class="cb-pdf__page"
1251
+ role="group"
1252
+ [attr.data-page]="i + 1"
1253
+ [attr.aria-label]="format(t().pageOf, i + 1, numPages())"
1254
+ [style.width.px]="box.width"
1255
+ [style.height.px]="box.height"
1256
+ [style.--scale-factor]="box.scaleFactor"
1257
+ [style.--user-unit]="box.userUnit"
1258
+ >
1259
+ <div class="cb-pdf__canvas"></div>
1260
+ <div class="cb-pdf__text"></div>
1261
+ </div>
1262
+ }
1263
+ </div>
1264
+ }
1265
+ }
1266
+ </div>
1267
+ </div>
1268
+
1269
+ @if (printProgress(); as p) {
1270
+ <div class="cb-pdf__toast" role="status">{{ format(t().preparingPrint, p.done, p.total) }}</div>
1271
+ }
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 }); }
1273
+ }
1274
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbPdfViewer, decorators: [{
1275
+ type: Component,
1276
+ args: [{ selector: 'cb-pdf-viewer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbIcon, CbSpinner], host: {
1277
+ class: 'cb-pdf-viewer',
1278
+ role: 'region',
1279
+ '[attr.aria-label]': 'ariaLabel() || t().pdfViewer',
1280
+ '(keydown)': 'onHostKeydown($event)',
1281
+ }, template: `
1282
+ @if (showToolbar()) {
1283
+ <div class="cb-pdf__toolbar">
1284
+ @if (showThumbnails()) {
1285
+ <div class="cb-pdf__group">
1286
+ <button
1287
+ type="button"
1288
+ class="cb-pdf__tool"
1289
+ [attr.aria-label]="t().thumbnails"
1290
+ [attr.aria-expanded]="sidebar()"
1291
+ [attr.aria-controls]="uid + '-thumbs'"
1292
+ [disabled]="state() !== 'ready'"
1293
+ (click)="sidebar.set(!sidebar())"
1294
+ >
1295
+ <cb-icon name="panel-left" [size]="18" aria-hidden="true" />
1296
+ </button>
1297
+ </div>
1298
+ }
1299
+
1300
+ <div class="cb-pdf__group" role="group" [attr.aria-label]="t().pagination">
1301
+ <button
1302
+ type="button"
1303
+ class="cb-pdf__tool"
1304
+ [attr.aria-label]="t().previousPage"
1305
+ [disabled]="state() !== 'ready' || page() <= 1"
1306
+ (click)="previousPage()"
1307
+ >
1308
+ <cb-icon name="chevron-up" [size]="18" aria-hidden="true" />
1309
+ </button>
1310
+ <button
1311
+ type="button"
1312
+ class="cb-pdf__tool"
1313
+ [attr.aria-label]="t().nextPage"
1314
+ [disabled]="state() !== 'ready' || page() >= numPages()"
1315
+ (click)="nextPage()"
1316
+ >
1317
+ <cb-icon name="chevron-down" [size]="18" aria-hidden="true" />
1318
+ </button>
1319
+ <input
1320
+ class="cb-pdf__field cb-pdf__page-input"
1321
+ type="text"
1322
+ inputmode="numeric"
1323
+ autocomplete="off"
1324
+ [id]="uid + '-page'"
1325
+ [attr.aria-label]="t().pageNumber"
1326
+ [attr.aria-describedby]="uid + '-total'"
1327
+ [value]="page()"
1328
+ [disabled]="state() !== 'ready'"
1329
+ (change)="onPageInput($event)"
1330
+ (keydown.enter)="onPageInput($event)"
1331
+ />
1332
+ <span class="cb-pdf__total" [id]="uid + '-total'">{{ format(t().ofTotal, numPages() || '–') }}</span>
1333
+ </div>
1334
+
1335
+ <div class="cb-pdf__group" role="group" [attr.aria-label]="t().zoomLevel">
1336
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().zoomOut" [disabled]="state() !== 'ready' || scale() <= 0.25" (click)="zoomOut()">
1337
+ <cb-icon name="zoom-out" [size]="18" aria-hidden="true" />
1338
+ </button>
1339
+ <select
1340
+ class="cb-pdf__field cb-pdf__zoom"
1341
+ [attr.aria-label]="t().zoomLevel"
1342
+ [disabled]="state() !== 'ready'"
1343
+ (change)="onZoomSelect($any($event.target).value)"
1344
+ >
1345
+ <option value="page-width" [selected]="zoomValue() === 'page-width'">{{ t().fitWidth }}</option>
1346
+ <option value="page-fit" [selected]="zoomValue() === 'page-fit'">{{ t().fitPage }}</option>
1347
+ @if (customZoom(); as custom) {
1348
+ <option [value]="custom" selected>{{ percent(custom) }}</option>
1349
+ }
1350
+ @for (level of zoomLevels; track level) {
1351
+ <option [value]="level" [selected]="zoomValue() === '' + level">{{ percent(level) }}</option>
1352
+ }
1353
+ </select>
1354
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().zoomIn" [disabled]="state() !== 'ready' || scale() >= 5" (click)="zoomIn()">
1355
+ <cb-icon name="zoom-in" [size]="18" aria-hidden="true" />
1356
+ </button>
1357
+ @if (showRotate()) {
1358
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().rotateRight" [disabled]="state() !== 'ready'" (click)="rotate()">
1359
+ <cb-icon name="rotate-cw" [size]="18" aria-hidden="true" />
1360
+ </button>
1361
+ }
1362
+ </div>
1363
+
1364
+ <span class="cb-pdf__spacer"></span>
1365
+
1366
+ @if (showSearch()) {
1367
+ <div class="cb-pdf__group cb-pdf__search" role="search">
1368
+ <cb-icon class="cb-pdf__search-icon" name="search" [size]="16" aria-hidden="true" />
1369
+ <input
1370
+ #searchInput
1371
+ class="cb-pdf__field cb-pdf__search-input"
1372
+ type="search"
1373
+ autocomplete="off"
1374
+ [attr.aria-label]="t().searchInDocument"
1375
+ [attr.aria-describedby]="uid + '-status'"
1376
+ [placeholder]="t().searchInDocument"
1377
+ [disabled]="state() !== 'ready'"
1378
+ (input)="onSearchInput(searchInput.value)"
1379
+ (keydown)="onSearchKeydown($event, searchInput.value)"
1380
+ />
1381
+ <span class="cb-pdf__status" [id]="uid + '-status'" aria-live="polite">{{ searchStatus() }}</span>
1382
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().previousMatch" [disabled]="!matches().length" (click)="findPrevious()">
1383
+ <cb-icon name="chevron-left" [size]="18" aria-hidden="true" />
1384
+ </button>
1385
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().nextMatch" [disabled]="!matches().length" (click)="findNext()">
1386
+ <cb-icon name="chevron-right" [size]="18" aria-hidden="true" />
1387
+ </button>
1388
+ </div>
1389
+ }
1390
+
1391
+ @if (downloadable() || printable()) {
1392
+ <div class="cb-pdf__group">
1393
+ @if (downloadable()) {
1394
+ <button type="button" class="cb-pdf__tool" [attr.aria-label]="t().download" [disabled]="state() !== 'ready'" (click)="download()">
1395
+ <cb-icon name="download" [size]="18" aria-hidden="true" />
1396
+ </button>
1397
+ }
1398
+ @if (printable()) {
1399
+ <button
1400
+ type="button"
1401
+ class="cb-pdf__tool"
1402
+ [attr.aria-label]="t().print"
1403
+ [disabled]="state() !== 'ready' || !!printProgress()"
1404
+ (click)="print()"
1405
+ >
1406
+ <cb-icon name="printer" [size]="18" aria-hidden="true" />
1407
+ </button>
1408
+ }
1409
+ </div>
1410
+ }
1411
+ </div>
1412
+ }
1413
+
1414
+ <div class="cb-pdf__body">
1415
+ @if (state() === 'ready' && sidebar() && showThumbnails()) {
1416
+ <nav class="cb-pdf__sidebar" [id]="uid + '-thumbs'" [attr.aria-label]="t().thumbnails">
1417
+ @for (thumb of thumbBoxes(); track $index; let i = $index) {
1418
+ <button
1419
+ #thumbEl
1420
+ type="button"
1421
+ class="cb-pdf__thumb"
1422
+ [class.cb-is-active]="page() === i + 1"
1423
+ [attr.data-page]="i + 1"
1424
+ [attr.aria-label]="format(t().pageOf, i + 1, numPages())"
1425
+ [attr.aria-current]="page() === i + 1 ? 'page' : null"
1426
+ (click)="goToPage(i + 1)"
1427
+ (keydown)="onThumbKeydown($event, i + 1)"
1428
+ >
1429
+ <span class="cb-pdf__thumb-canvas" [style.width.px]="thumb.width" [style.height.px]="thumb.height"></span>
1430
+ <span class="cb-pdf__thumb-label" aria-hidden="true">{{ i + 1 }}</span>
1431
+ </button>
1432
+ }
1433
+ </nav>
1434
+ }
1435
+
1436
+ <div
1437
+ #viewport
1438
+ class="cb-pdf__viewport"
1439
+ tabindex="0"
1440
+ role="document"
1441
+ [attr.aria-label]="t().pdfDocument"
1442
+ [attr.aria-busy]="state() === 'loading' ? 'true' : null"
1443
+ (scroll)="onScroll()"
1444
+ (wheel)="onWheel($event)"
1445
+ >
1446
+ @switch (state()) {
1447
+ @case ('loading') {
1448
+ <div class="cb-pdf__message" role="status">
1449
+ <cb-spinner [size]="32" aria-hidden="true" />
1450
+ <span>{{ t().loadingDocument }}@if (progress() !== null) { {{ progress() }} %}</span>
1451
+ </div>
1452
+ }
1453
+ @case ('error') {
1454
+ <div class="cb-pdf__message cb-pdf__message--error" role="alert">{{ t().documentError }}</div>
1455
+ }
1456
+ @case ('password') {
1457
+ <form class="cb-pdf__password" (submit)="submitPassword($event, passwordInput.value)">
1458
+ <cb-icon name="lock" [size]="28" aria-hidden="true" />
1459
+ <p class="cb-pdf__password-text" [id]="uid + '-pwd-text'">{{ t().protectedDocument }}</p>
1460
+ <label class="cb-pdf__password-label" [for]="uid + '-pwd'">{{ t().password }}</label>
1461
+ <div class="cb-pdf__password-row">
1462
+ <input
1463
+ #passwordInput
1464
+ class="cb-pdf__field"
1465
+ type="password"
1466
+ autocomplete="off"
1467
+ [id]="uid + '-pwd'"
1468
+ [attr.aria-describedby]="passwordIncorrect() ? uid + '-pwd-error' : uid + '-pwd-text'"
1469
+ [attr.aria-invalid]="passwordIncorrect() ? 'true' : null"
1470
+ />
1471
+ <button type="submit" class="cb-pdf__submit">{{ t().open }}</button>
1472
+ </div>
1473
+ @if (passwordIncorrect()) {
1474
+ <p class="cb-pdf__password-error" [id]="uid + '-pwd-error'" role="alert">{{ t().incorrectPassword }}</p>
1475
+ }
1476
+ </form>
1477
+ }
1478
+ @case ('ready') {
1479
+ <div class="cb-pdf__pages">
1480
+ @for (box of pageBoxes(); track $index; let i = $index) {
1481
+ <div
1482
+ #pageEl
1483
+ class="cb-pdf__page"
1484
+ role="group"
1485
+ [attr.data-page]="i + 1"
1486
+ [attr.aria-label]="format(t().pageOf, i + 1, numPages())"
1487
+ [style.width.px]="box.width"
1488
+ [style.height.px]="box.height"
1489
+ [style.--scale-factor]="box.scaleFactor"
1490
+ [style.--user-unit]="box.userUnit"
1491
+ >
1492
+ <div class="cb-pdf__canvas"></div>
1493
+ <div class="cb-pdf__text"></div>
1494
+ </div>
1495
+ }
1496
+ </div>
1497
+ }
1498
+ }
1499
+ </div>
1500
+ </div>
1501
+
1502
+ @if (printProgress(); as p) {
1503
+ <div class="cb-pdf__toast" role="status">{{ format(t().preparingPrint, p.done, p.total) }}</div>
1504
+ }
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 }] }] } });
1507
+ function sizeOf(page) {
1508
+ const viewport = page.getViewport({ scale: 1, rotation: 0 });
1509
+ const userUnit = page.userUnit || 1;
1510
+ return { width: viewport.width, height: viewport.height, rotate: page.rotate || 0, userUnit };
1511
+ }
1512
+ /** Bytes del documento si no es una URL (copiados: pdf.js transfiere el buffer al worker). */
1513
+ async function toData(src) {
1514
+ if (src instanceof Uint8Array)
1515
+ return src.slice();
1516
+ if (src instanceof ArrayBuffer)
1517
+ return new Uint8Array(src.slice(0));
1518
+ if (typeof Blob !== 'undefined' && src instanceof Blob)
1519
+ return new Uint8Array(await readBlob(src));
1520
+ return null;
1521
+ }
1522
+ function readBlob(blob) {
1523
+ if (typeof blob.arrayBuffer === 'function')
1524
+ return blob.arrayBuffer();
1525
+ return new Promise((resolve, reject) => {
1526
+ const reader = new FileReader();
1527
+ reader.onload = () => resolve(reader.result);
1528
+ reader.onerror = () => reject(reader.error);
1529
+ reader.readAsArrayBuffer(blob);
1530
+ });
1531
+ }
1532
+ /** Densidad del canvas: la de la pantalla, limitada para no pasar de {@link MAX_CANVAS_PIXELS}. */
1533
+ function outputScale(width, height) {
1534
+ const ratio = globalThis.devicePixelRatio || 1;
1535
+ const max = Math.sqrt(MAX_CANVAS_PIXELS / Math.max(1, width * height));
1536
+ return Math.max(0.5, Math.min(ratio, max));
1537
+ }
1538
+ function clamp(value, min, max) {
1539
+ return Math.min(max, Math.max(min, value));
1540
+ }
1541
+ function round2(value) {
1542
+ return Math.round(value * 100) / 100;
1543
+ }
1544
+
1545
+ /*
1546
+ * Public API Surface de @caribeng/ui/pdfviewer
1547
+ */
1548
+
1549
+ /**
1550
+ * Generated bundle index. Do not edit.
1551
+ */
1552
+
1553
+ export { CB_PDF_VIEWER_OPTIONS, CbPdfEngine, CbPdfViewer, provideCbPdfViewer };