@sdcorejs/angular 19.1.6 → 19.2.1

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 (374) hide show
  1. package/README.md +10 -0
  2. package/assets/scss/core/menu.scss +18 -0
  3. package/assets/scss/core/utilities/_misc.scss +15 -0
  4. package/assets/scss/sd-core.scss +1 -0
  5. package/components/anchor/src/components/anchor/anchor.component.d.ts +3 -1
  6. package/components/api-contract-builder/index.d.ts +8 -0
  7. package/components/api-contract-builder/src/api-contract-builder.component.d.ts +123 -0
  8. package/components/api-contract-builder/src/api-contract.configuration.d.ts +41 -0
  9. package/components/api-contract-builder/src/api-contract.expression.d.ts +43 -0
  10. package/components/api-contract-builder/src/api-contract.model.d.ts +167 -0
  11. package/components/api-contract-builder/src/api-contract.samples.d.ts +25 -0
  12. package/components/api-contract-builder/src/api-contract.schema.d.ts +126 -0
  13. package/components/api-contract-builder/src/api-contract.serializer.d.ts +17 -0
  14. package/components/api-contract-builder/src/api-contract.validation.d.ts +12 -0
  15. package/components/api-contract-builder/src/components/api-contract-diagnostic-list.component.d.ts +18 -0
  16. package/components/api-contract-builder/src/components/api-contract-node-drawer.component.d.ts +130 -0
  17. package/components/api-contract-builder/src/components/api-contract-node-editor.component.d.ts +35 -0
  18. package/components/api-contract-builder/src/components/api-contract-node-summary.component.d.ts +41 -0
  19. package/components/api-contract-builder/src/components/api-contract-record-editor.component.d.ts +36 -0
  20. package/components/api-contract-builder/src/components/api-contract-source-editor.component.d.ts +86 -0
  21. package/components/api-contract-builder/src/components/api-contract-suggestion.model.d.ts +17 -0
  22. package/components/badge/src/badge.component.d.ts +2 -0
  23. package/components/ckeditor-styles/src/ckeditor-styles.component.d.ts +1 -1
  24. package/components/form-generic/src/components/form-builder/components/expression-builder/expression-builder.component.d.ts +2 -1
  25. package/components/form-generic/src/components/form-builder/form-builder.component.d.ts +3 -1
  26. package/components/form-generic/src/components/form-render/components/item/components/checkbox/checkbox.component.d.ts +34 -0
  27. package/components/form-generic/src/components/form-render/components/item/components/index.d.ts +1 -0
  28. package/components/form-generic/src/components/form-render/components/item/components/table/table.component.d.ts +6 -1
  29. package/components/form-generic/src/components/sd-feel-expression/sd-feel-expression.component.d.ts +1 -1
  30. package/components/form-generic/src/configurations/form.configuration.d.ts +1 -1
  31. package/components/form-generic/src/models/form-generic-component.model.d.ts +8 -8
  32. package/components/form-generic/src/models/form-generic-definition-selection.model.d.ts +5 -5
  33. package/components/form-generic/src/models/form-generic-expression.model.d.ts +12 -12
  34. package/components/form-generic/src/models/index.d.ts +1 -0
  35. package/components/form-generic/src/services/form-generic.service.d.ts +2 -2
  36. package/components/import-excel/src/import-excel.component.d.ts +1 -0
  37. package/components/index.d.ts +7 -1
  38. package/components/mini-editor/src/mini-editor.model.d.ts +1 -1
  39. package/components/modal/src/modal.component.d.ts +3 -2
  40. package/components/modal-resizable/src/modal-resizable.component.d.ts +92 -0
  41. package/components/modal-resizable/src/modal-resizable.registry.d.ts +22 -0
  42. package/components/org-chart/src/org-chart.component.d.ts +6 -0
  43. package/components/preview/src/preview-image/preview-image.component.d.ts +1 -0
  44. package/components/query-bar/src/components/actions-bar/actions-bar.component.d.ts +6 -2
  45. package/components/query-bar/src/components/build-chip/build-chip.component.d.ts +4 -1
  46. package/components/query-bar/src/components/chip-popover/chip-popover.component.d.ts +8 -0
  47. package/components/query-bar/src/components/field-picker/field-picker.component.d.ts +2 -0
  48. package/components/query-bar/src/components/inline-chip/inline-chip.component.d.ts +2 -0
  49. package/components/query-bar/src/components/inline-value-chip/inline-value-chip.component.d.ts +3 -3
  50. package/components/query-bar/src/components/saved-filters-menu/saved-filters-menu.component.d.ts +9 -4
  51. package/components/query-bar/src/query-bar.component.d.ts +32 -5
  52. package/components/query-bar/src/query-bar.model.d.ts +15 -12
  53. package/components/query-builder/src/query-builder.component.d.ts +76 -39
  54. package/components/query-builder/src/query-builder.model.d.ts +59 -52
  55. package/components/query-builder/src/query-builder.serializer.d.ts +15 -6
  56. package/components/section/src/section.component.d.ts +5 -0
  57. package/components/side-drawer/src/body-scroll-lock.service.d.ts +10 -0
  58. package/components/splitter/src/splitter-handle/splitter-handle.component.d.ts +1 -0
  59. package/components/tab-router/index.d.ts +2 -1
  60. package/components/tab-router/src/actions/tab-router.action.d.ts +2 -2
  61. package/components/tab-router/src/components/tab-router-item/tab-router-item.component.d.ts +2 -2
  62. package/components/tab-router/src/components/tab-router-nav/tab-router-nav.component.d.ts +3 -3
  63. package/components/tab-router/src/components/tab-router-outlet/tab-router-outlet.component.d.ts +3 -3
  64. package/components/tab-router/src/decorators/tab.decorator.d.ts +13 -1
  65. package/components/tab-router/src/events/tab-router.event.d.ts +3 -3
  66. package/components/tab-router/src/models/tab-router.model.d.ts +2 -2
  67. package/components/tab-router/src/pipes/tab-info.pipe.d.ts +2 -2
  68. package/components/tab-router/src/services/tab-router.service.d.ts +9 -9
  69. package/components/table/index.d.ts +1 -0
  70. package/components/table/src/components/config/config.component.d.ts +6 -1
  71. package/components/table/src/components/desktop-cell/view/view.component.d.ts +1 -0
  72. package/components/table/src/components/filter/external-filter/external-filter.component.d.ts +1 -1
  73. package/components/table/src/components/filter/mobile-filter/mobile-filter.component.d.ts +1 -1
  74. package/components/table/src/components/selector-action/action-filter.pipe.d.ts +8 -1
  75. package/components/table/src/components/selector-action/selector-action.component.d.ts +2 -0
  76. package/components/table/src/directives/index.d.ts +1 -0
  77. package/components/table/src/directives/sd-table-command-header-def.directive.d.ts +24 -0
  78. package/components/table/src/models/table-column.model.d.ts +30 -13
  79. package/components/table/src/models/table-item.model.d.ts +30 -1
  80. package/components/table/src/models/table-option-selector.model.d.ts +5 -4
  81. package/components/table/src/models/table-option.model.d.ts +12 -0
  82. package/components/table/src/pipes/sd-group.pipe.d.ts +6 -2
  83. package/components/table/src/services/table-filter/table-filter.model.d.ts +15 -15
  84. package/components/table/src/services/table-filter/table-filter.service.d.ts +2 -2
  85. package/components/table/src/services/table-format/table-format.service.d.ts +1 -1
  86. package/components/table/src/services/table-selection/selection-action.util.d.ts +24 -0
  87. package/components/table/src/services/table-selection/table-selection.util.d.ts +31 -0
  88. package/components/table/src/services/tree/tree.util.d.ts +3 -2
  89. package/components/table/src/table.component.d.ts +51 -5
  90. package/components/tree/src/tree-item-def.directive.d.ts +1 -1
  91. package/components/tree/src/tree.component.d.ts +16 -4
  92. package/components/tree/src/tree.model.d.ts +31 -25
  93. package/components/upload-file/src/configurations/upload-file.configuration.d.ts +2 -2
  94. package/components/upload-file/src/upload-file.component.d.ts +4 -1
  95. package/configurations/src/sd-core.configuration.d.ts +1 -2
  96. package/fesm2022/sdcorejs-angular-components-anchor.mjs +36 -12
  97. package/fesm2022/sdcorejs-angular-components-anchor.mjs.map +1 -1
  98. package/fesm2022/sdcorejs-angular-components-api-contract-builder.mjs +3411 -0
  99. package/fesm2022/sdcorejs-angular-components-api-contract-builder.mjs.map +1 -0
  100. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs +2 -2
  101. package/fesm2022/sdcorejs-angular-components-autoid-inspector.mjs.map +1 -1
  102. package/fesm2022/sdcorejs-angular-components-badge.mjs +27 -2
  103. package/fesm2022/sdcorejs-angular-components-badge.mjs.map +1 -1
  104. package/fesm2022/sdcorejs-angular-components-button.mjs +2 -2
  105. package/fesm2022/sdcorejs-angular-components-button.mjs.map +1 -1
  106. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs +1 -1
  107. package/fesm2022/sdcorejs-angular-components-ckeditor-styles.mjs.map +1 -1
  108. package/fesm2022/sdcorejs-angular-components-code-editor.mjs +3 -3
  109. package/fesm2022/sdcorejs-angular-components-code-editor.mjs.map +1 -1
  110. package/fesm2022/sdcorejs-angular-components-data-state.mjs +3 -3
  111. package/fesm2022/sdcorejs-angular-components-data-state.mjs.map +1 -1
  112. package/fesm2022/sdcorejs-angular-components-editor.mjs +2 -2
  113. package/fesm2022/sdcorejs-angular-components-editor.mjs.map +1 -1
  114. package/fesm2022/sdcorejs-angular-components-form-generic.mjs +324 -192
  115. package/fesm2022/sdcorejs-angular-components-form-generic.mjs.map +1 -1
  116. package/fesm2022/sdcorejs-angular-components-history.mjs +4 -4
  117. package/fesm2022/sdcorejs-angular-components-history.mjs.map +1 -1
  118. package/fesm2022/sdcorejs-angular-components-import-excel.mjs +13 -5
  119. package/fesm2022/sdcorejs-angular-components-import-excel.mjs.map +1 -1
  120. package/fesm2022/sdcorejs-angular-components-inform.mjs +3 -3
  121. package/fesm2022/sdcorejs-angular-components-inform.mjs.map +1 -1
  122. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs +164 -8
  123. package/fesm2022/sdcorejs-angular-components-modal-resizable.mjs.map +1 -1
  124. package/fesm2022/sdcorejs-angular-components-modal.mjs +30 -18
  125. package/fesm2022/sdcorejs-angular-components-modal.mjs.map +1 -1
  126. package/fesm2022/sdcorejs-angular-components-operator.mjs +2 -2
  127. package/fesm2022/sdcorejs-angular-components-operator.mjs.map +1 -1
  128. package/fesm2022/sdcorejs-angular-components-org-chart.mjs +60 -13
  129. package/fesm2022/sdcorejs-angular-components-org-chart.mjs.map +1 -1
  130. package/fesm2022/sdcorejs-angular-components-preview.mjs +27 -17
  131. package/fesm2022/sdcorejs-angular-components-preview.mjs.map +1 -1
  132. package/fesm2022/sdcorejs-angular-components-query-bar.mjs +192 -30
  133. package/fesm2022/sdcorejs-angular-components-query-bar.mjs.map +1 -1
  134. package/fesm2022/sdcorejs-angular-components-query-builder.mjs +187 -127
  135. package/fesm2022/sdcorejs-angular-components-query-builder.mjs.map +1 -1
  136. package/fesm2022/sdcorejs-angular-components-section.mjs +33 -2
  137. package/fesm2022/sdcorejs-angular-components-section.mjs.map +1 -1
  138. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs +83 -24
  139. package/fesm2022/sdcorejs-angular-components-side-drawer.mjs.map +1 -1
  140. package/fesm2022/sdcorejs-angular-components-splitter.mjs +57 -21
  141. package/fesm2022/sdcorejs-angular-components-splitter.mjs.map +1 -1
  142. package/fesm2022/sdcorejs-angular-components-stepper.mjs +2 -2
  143. package/fesm2022/sdcorejs-angular-components-stepper.mjs.map +1 -1
  144. package/fesm2022/sdcorejs-angular-components-tab-router.mjs +68 -20
  145. package/fesm2022/sdcorejs-angular-components-tab-router.mjs.map +1 -1
  146. package/fesm2022/sdcorejs-angular-components-tab.mjs +3 -2
  147. package/fesm2022/sdcorejs-angular-components-tab.mjs.map +1 -1
  148. package/fesm2022/sdcorejs-angular-components-table.mjs +555 -267
  149. package/fesm2022/sdcorejs-angular-components-table.mjs.map +1 -1
  150. package/fesm2022/sdcorejs-angular-components-tree.mjs +80 -17
  151. package/fesm2022/sdcorejs-angular-components-tree.mjs.map +1 -1
  152. package/fesm2022/sdcorejs-angular-components-upload-file.mjs +48 -13
  153. package/fesm2022/sdcorejs-angular-components-upload-file.mjs.map +1 -1
  154. package/fesm2022/sdcorejs-angular-components.mjs +7 -1
  155. package/fesm2022/sdcorejs-angular-components.mjs.map +1 -1
  156. package/fesm2022/sdcorejs-angular-configurations.mjs.map +1 -1
  157. package/fesm2022/sdcorejs-angular-directives.mjs +45 -8
  158. package/fesm2022/sdcorejs-angular-directives.mjs.map +1 -1
  159. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs +40 -29
  160. package/fesm2022/sdcorejs-angular-forms-autocomplete.mjs.map +1 -1
  161. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs +5 -5
  162. package/fesm2022/sdcorejs-angular-forms-checkbox.mjs.map +1 -1
  163. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs +67 -50
  164. package/fesm2022/sdcorejs-angular-forms-chip-calendar.mjs.map +1 -1
  165. package/fesm2022/sdcorejs-angular-forms-chip.mjs +57 -43
  166. package/fesm2022/sdcorejs-angular-forms-chip.mjs.map +1 -1
  167. package/fesm2022/sdcorejs-angular-forms-date-range.mjs +231 -60
  168. package/fesm2022/sdcorejs-angular-forms-date-range.mjs.map +1 -1
  169. package/fesm2022/sdcorejs-angular-forms-date.mjs +95 -23
  170. package/fesm2022/sdcorejs-angular-forms-date.mjs.map +1 -1
  171. package/fesm2022/sdcorejs-angular-forms-datetime.mjs +130 -26
  172. package/fesm2022/sdcorejs-angular-forms-datetime.mjs.map +1 -1
  173. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs +39 -15
  174. package/fesm2022/sdcorejs-angular-forms-entity-picker.mjs.map +1 -1
  175. package/fesm2022/sdcorejs-angular-forms-input-color.mjs +16 -5
  176. package/fesm2022/sdcorejs-angular-forms-input-color.mjs.map +1 -1
  177. package/fesm2022/sdcorejs-angular-forms-input-number.mjs +24 -9
  178. package/fesm2022/sdcorejs-angular-forms-input-number.mjs.map +1 -1
  179. package/fesm2022/sdcorejs-angular-forms-input.mjs +58 -10
  180. package/fesm2022/sdcorejs-angular-forms-input.mjs.map +1 -1
  181. package/fesm2022/sdcorejs-angular-forms-models.mjs +227 -6
  182. package/fesm2022/sdcorejs-angular-forms-models.mjs.map +1 -1
  183. package/fesm2022/sdcorejs-angular-forms-radio.mjs +4 -4
  184. package/fesm2022/sdcorejs-angular-forms-radio.mjs.map +1 -1
  185. package/fesm2022/sdcorejs-angular-forms-select.mjs +135 -37
  186. package/fesm2022/sdcorejs-angular-forms-select.mjs.map +1 -1
  187. package/fesm2022/sdcorejs-angular-forms-switch.mjs +18 -3
  188. package/fesm2022/sdcorejs-angular-forms-switch.mjs.map +1 -1
  189. package/fesm2022/sdcorejs-angular-forms-textarea.mjs +51 -62
  190. package/fesm2022/sdcorejs-angular-forms-textarea.mjs.map +1 -1
  191. package/fesm2022/sdcorejs-angular-forms-time-range.mjs +57 -12
  192. package/fesm2022/sdcorejs-angular-forms-time-range.mjs.map +1 -1
  193. package/fesm2022/sdcorejs-angular-forms-time.mjs +16 -5
  194. package/fesm2022/sdcorejs-angular-forms-time.mjs.map +1 -1
  195. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs +100 -9
  196. package/fesm2022/sdcorejs-angular-forms-tree-select.mjs.map +1 -1
  197. package/fesm2022/sdcorejs-angular-forms.mjs +91 -46
  198. package/fesm2022/sdcorejs-angular-forms.mjs.map +1 -1
  199. package/fesm2022/sdcorejs-angular-handlers.mjs +18 -3
  200. package/fesm2022/sdcorejs-angular-handlers.mjs.map +1 -1
  201. package/fesm2022/sdcorejs-angular-i18n.mjs +895 -68
  202. package/fesm2022/sdcorejs-angular-i18n.mjs.map +1 -1
  203. package/fesm2022/sdcorejs-angular-interceptors.mjs +87 -29
  204. package/fesm2022/sdcorejs-angular-interceptors.mjs.map +1 -1
  205. package/fesm2022/sdcorejs-angular-modules-auth.mjs +25 -15
  206. package/fesm2022/sdcorejs-angular-modules-auth.mjs.map +1 -1
  207. package/fesm2022/sdcorejs-angular-modules-icon.mjs +2 -2
  208. package/fesm2022/sdcorejs-angular-modules-icon.mjs.map +1 -1
  209. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs +32 -3
  210. package/fesm2022/sdcorejs-angular-modules-keycloak.mjs.map +1 -1
  211. package/fesm2022/sdcorejs-angular-modules-layout.mjs +342 -111
  212. package/fesm2022/sdcorejs-angular-modules-layout.mjs.map +1 -1
  213. package/fesm2022/sdcorejs-angular-modules-permission.mjs +177 -40
  214. package/fesm2022/sdcorejs-angular-modules-permission.mjs.map +1 -1
  215. package/fesm2022/sdcorejs-angular-pipes.mjs +62 -28
  216. package/fesm2022/sdcorejs-angular-pipes.mjs.map +1 -1
  217. package/fesm2022/sdcorejs-angular-services-api.mjs +123 -13
  218. package/fesm2022/sdcorejs-angular-services-api.mjs.map +1 -1
  219. package/fesm2022/sdcorejs-angular-services-cache.mjs +53 -3
  220. package/fesm2022/sdcorejs-angular-services-cache.mjs.map +1 -1
  221. package/fesm2022/sdcorejs-angular-services-confirm.mjs +25 -73
  222. package/fesm2022/sdcorejs-angular-services-confirm.mjs.map +1 -1
  223. package/fesm2022/sdcorejs-angular-services-excel.mjs +12 -10
  224. package/fesm2022/sdcorejs-angular-services-excel.mjs.map +1 -1
  225. package/fesm2022/sdcorejs-angular-services-loading.mjs +75 -84
  226. package/fesm2022/sdcorejs-angular-services-loading.mjs.map +1 -1
  227. package/fesm2022/sdcorejs-angular-services-notify.mjs +46 -5
  228. package/fesm2022/sdcorejs-angular-services-notify.mjs.map +1 -1
  229. package/fesm2022/sdcorejs-angular-services-storage.mjs +4 -0
  230. package/fesm2022/sdcorejs-angular-services-storage.mjs.map +1 -1
  231. package/fesm2022/sdcorejs-angular-services.mjs +0 -1
  232. package/fesm2022/sdcorejs-angular-services.mjs.map +1 -1
  233. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs +225 -80
  234. package/fesm2022/sdcorejs-angular-utilities-extensions.mjs.map +1 -1
  235. package/fesm2022/sdcorejs-angular-utilities-models.mjs +1 -63
  236. package/fesm2022/sdcorejs-angular-utilities-models.mjs.map +1 -1
  237. package/fesm2022/sdcorejs-angular.mjs +17 -10
  238. package/fesm2022/sdcorejs-angular.mjs.map +1 -1
  239. package/forms/autocomplete/src/autocomplete.component.d.ts +5 -2
  240. package/forms/chip-calendar/src/chip-calendar.component.d.ts +10 -0
  241. package/forms/date/src/date.component.d.ts +13 -2
  242. package/forms/date-range/src/date-range.component.d.ts +13 -2
  243. package/forms/datetime/src/datetime.component.d.ts +13 -2
  244. package/forms/entity-picker/src/entity-picker.component.d.ts +9 -3
  245. package/forms/input/src/input.component.d.ts +3 -0
  246. package/forms/input-color/src/input-color.component.d.ts +4 -0
  247. package/forms/input-number/src/input-number.component.d.ts +3 -0
  248. package/forms/models/index.d.ts +3 -0
  249. package/forms/models/src/sd-form-control-connector.d.ts +12 -0
  250. package/forms/models/src/sd-model-facing-control.d.ts +43 -0
  251. package/forms/models/src/sd-search.model.d.ts +1 -1
  252. package/forms/models/src/sd-selection-data.model.d.ts +1 -1
  253. package/forms/models/src/sd-temporal-transform.d.ts +31 -0
  254. package/forms/models/src/sd-timer-scope.d.ts +29 -0
  255. package/forms/select/src/select.component.d.ts +27 -4
  256. package/forms/src/forms.module.d.ts +22 -13
  257. package/forms/switch/src/switch.component.d.ts +5 -0
  258. package/forms/textarea/src/textarea.component.d.ts +5 -3
  259. package/forms/time-range/src/time-range.component.d.ts +22 -1
  260. package/forms/tree-select/src/tree-select.component.d.ts +36 -5
  261. package/i18n/src/en.d.ts +170 -10
  262. package/i18n/src/i18n.messages.d.ts +1 -1
  263. package/i18n/src/i18n.pipe.d.ts +3 -3
  264. package/i18n/src/i18n.service.d.ts +2 -1
  265. package/i18n/src/i18n.types.d.ts +0 -3
  266. package/interceptors/no-internet/no-internet.interceptor.d.ts +11 -1
  267. package/modules/auth/services/auth.model.d.ts +1 -1
  268. package/modules/keycloak/keycloak.service.d.ts +1 -0
  269. package/modules/layout/components/shared/menu-tree/menu-tree.component.d.ts +2 -0
  270. package/modules/layout/components/sidebar-mobile-v1/components/sidebar/sidebar.component.d.ts +3 -3
  271. package/modules/layout/components/sidebar-mobile-v1/main.component.d.ts +3 -3
  272. package/modules/layout/components/sidebar-mobile-v2/main.component.d.ts +3 -3
  273. package/modules/layout/components/sidebar-mobile-v3/main.component.d.ts +3 -3
  274. package/modules/layout/components/sidebar-v1/components/sidebar/sidebar.component.d.ts +16 -3
  275. package/modules/layout/components/sidebar-v1/main.component.d.ts +3 -3
  276. package/modules/layout/components/sidebar-v2/main.component.d.ts +3 -3
  277. package/modules/layout/components/sidebar-v3/main.component.d.ts +3 -3
  278. package/modules/layout/modules/forbidden/pages/root/root.component.d.ts +5 -0
  279. package/modules/layout/modules/not-found/pages/root/root.component.d.ts +5 -0
  280. package/modules/layout/services/layout.service.d.ts +21 -0
  281. package/modules/layout/services/storage/storage.service.d.ts +52 -7
  282. package/modules/permission/src/configurations/permission.configuration.d.ts +11 -0
  283. package/modules/permission/src/directives/permission.directive.d.ts +2 -1
  284. package/modules/permission/src/services/permission.service.d.ts +52 -1
  285. package/package.json +40 -29
  286. package/pipes/src/safe-html.pipe.d.ts +26 -4
  287. package/pipes/src/time-different.pipe.d.ts +1 -0
  288. package/public-api.d.ts +11 -10
  289. package/services/api/src/api-host.d.ts +15 -0
  290. package/services/api/src/api.model.d.ts +14 -0
  291. package/services/api/src/api.module.d.ts +6 -0
  292. package/services/cache/src/cache.model.d.ts +10 -0
  293. package/services/index.d.ts +0 -1
  294. package/services/notify/index.d.ts +1 -1
  295. package/utilities/extensions/index.d.ts +1 -5
  296. package/utilities/extensions/src/url-safety.d.ts +72 -0
  297. package/utilities/extensions/src/utility.extension.d.ts +9 -5
  298. package/utilities/models/index.d.ts +0 -10
  299. package/components/base/index.d.ts +0 -1
  300. package/components/base/src/base-secure.component.d.ts +0 -8
  301. package/components/document-builder/index.d.ts +0 -4
  302. package/components/document-builder/src/components/header-footer-builder/header-footer-builder.component.d.ts +0 -15
  303. package/components/document-builder/src/components/header-footer-builder/plugins/page-number.plugin.d.ts +0 -4
  304. package/components/document-builder/src/components/index.d.ts +0 -1
  305. package/components/document-builder/src/document-builder.component.d.ts +0 -53
  306. package/components/document-builder/src/document-builder.config.d.ts +0 -21
  307. package/components/document-builder/src/document-builder.model.d.ts +0 -41
  308. package/components/document-builder/src/document-builder.utils.d.ts +0 -10
  309. package/components/document-builder/src/plugins/block-space/block-space.plugin.d.ts +0 -9
  310. package/components/document-builder/src/plugins/ck-comment/ck-comment.plugin.d.ts +0 -44
  311. package/components/document-builder/src/plugins/ck-comment/ck-comment.plugin.model.d.ts +0 -57
  312. package/components/document-builder/src/plugins/heading/heading.plugin.d.ts +0 -5
  313. package/components/document-builder/src/plugins/highlight-range/highlight-range.plugin.d.ts +0 -4
  314. package/components/document-builder/src/plugins/image-custom/image-custom.plugin.d.ts +0 -31
  315. package/components/document-builder/src/plugins/image-upload/image-upload.plugin.d.ts +0 -5
  316. package/components/document-builder/src/plugins/index.d.ts +0 -11
  317. package/components/document-builder/src/plugins/page-orientation/page-orientation.plugin.d.ts +0 -24
  318. package/components/document-builder/src/plugins/paste-handler/filters/bookmark.d.ts +0 -14
  319. package/components/document-builder/src/plugins/paste-handler/filters/br.d.ts +0 -15
  320. package/components/document-builder/src/plugins/paste-handler/filters/image.d.ts +0 -25
  321. package/components/document-builder/src/plugins/paste-handler/filters/list.d.ts +0 -29
  322. package/components/document-builder/src/plugins/paste-handler/filters/parse.d.ts +0 -35
  323. package/components/document-builder/src/plugins/paste-handler/filters/removeboldwrapper.d.ts +0 -15
  324. package/components/document-builder/src/plugins/paste-handler/filters/removegooglesheetstag.d.ts +0 -15
  325. package/components/document-builder/src/plugins/paste-handler/filters/removeinvalidtablewidth.d.ts +0 -15
  326. package/components/document-builder/src/plugins/paste-handler/filters/removemsattributes.d.ts +0 -15
  327. package/components/document-builder/src/plugins/paste-handler/filters/removestyleblock.d.ts +0 -15
  328. package/components/document-builder/src/plugins/paste-handler/filters/removexmlns.d.ts +0 -15
  329. package/components/document-builder/src/plugins/paste-handler/filters/replacemsfootnotes.d.ts +0 -54
  330. package/components/document-builder/src/plugins/paste-handler/filters/space.d.ts +0 -27
  331. package/components/document-builder/src/plugins/paste-handler/filters/table.d.ts +0 -16
  332. package/components/document-builder/src/plugins/paste-handler/filters/utils.d.ts +0 -25
  333. package/components/document-builder/src/plugins/paste-handler/index.d.ts +0 -35
  334. package/components/document-builder/src/plugins/paste-handler/normalizers/googledocsnormalizer.d.ts +0 -31
  335. package/components/document-builder/src/plugins/paste-handler/normalizers/googlesheetsnormalizer.d.ts +0 -31
  336. package/components/document-builder/src/plugins/paste-handler/normalizers/mswordnormalizer.d.ts +0 -29
  337. package/components/document-builder/src/plugins/paste-handler/types.d.ts +0 -30
  338. package/components/document-builder/src/plugins/table-custom/index.d.ts +0 -34
  339. package/components/document-builder/src/plugins/variable/variable.plugin.d.ts +0 -51
  340. package/components/table/src/pipes/selection-visible-select-all.pipe.d.ts +0 -9
  341. package/fesm2022/sdcorejs-angular-components-base.mjs +0 -23
  342. package/fesm2022/sdcorejs-angular-components-base.mjs.map +0 -1
  343. package/fesm2022/sdcorejs-angular-components-document-builder.mjs +0 -5038
  344. package/fesm2022/sdcorejs-angular-components-document-builder.mjs.map +0 -1
  345. package/fesm2022/sdcorejs-angular-models.mjs +0 -6
  346. package/fesm2022/sdcorejs-angular-models.mjs.map +0 -1
  347. package/fesm2022/sdcorejs-angular-services-docx.mjs +0 -291
  348. package/fesm2022/sdcorejs-angular-services-docx.mjs.map +0 -1
  349. package/fesm2022/sdcorejs-angular-services-license.mjs +0 -97
  350. package/fesm2022/sdcorejs-angular-services-license.mjs.map +0 -1
  351. package/models/index.d.ts +0 -1
  352. package/models/src/language.model.d.ts +0 -2
  353. package/services/docx/index.d.ts +0 -1
  354. package/services/docx/src/lib/docx.model.d.ts +0 -9
  355. package/services/docx/src/lib/docx.service.d.ts +0 -14
  356. package/services/docx/src/lib/pandoc-core.d.ts +0 -11
  357. package/services/docx/src/public-api.d.ts +0 -2
  358. package/services/license/index.d.ts +0 -1
  359. package/services/license/src/license.service.d.ts +0 -9
  360. package/utilities/extensions/src/array.extension.d.ts +0 -1
  361. package/utilities/extensions/src/color.extension.d.ts +0 -5
  362. package/utilities/extensions/src/date.extension.d.ts +0 -1
  363. package/utilities/extensions/src/number.extension.d.ts +0 -1
  364. package/utilities/extensions/src/string.extension.d.ts +0 -50
  365. package/utilities/models/src/color.model.d.ts +0 -4
  366. package/utilities/models/src/empty.model.d.ts +0 -4
  367. package/utilities/models/src/filter.model.d.ts +0 -12
  368. package/utilities/models/src/maybe-async.model.d.ts +0 -10
  369. package/utilities/models/src/nested-key-of.model.d.ts +0 -4
  370. package/utilities/models/src/operator.model.d.ts +0 -13
  371. package/utilities/models/src/order.model.d.ts +0 -4
  372. package/utilities/models/src/paging.model.d.ts +0 -8
  373. package/utilities/models/src/pattern.model.d.ts +0 -21
  374. package/utilities/models/src/size.model.d.ts +0 -4
@@ -1,14 +1,17 @@
1
1
  import * as i1 from '@angular/common';
2
- import { CommonModule, isPlatformBrowser, DOCUMENT } from '@angular/common';
2
+ import { DOCUMENT, CommonModule, isPlatformBrowser } from '@angular/common';
3
3
  import * as i0 from '@angular/core';
4
4
  import { inject, ElementRef, DestroyRef, input, computed, output, signal, effect, afterNextRender, HostListener, ChangeDetectionStrategy, Component, InjectionToken, PLATFORM_ID, viewChild, viewChildren } from '@angular/core';
5
5
  import { Utilities } from '@sdcorejs/utils/fns';
6
- import { TranslatePipe } from '@sdcorejs/angular/i18n';
6
+ import { SdTranslatePipe } from '@sdcorejs/angular/i18n';
7
7
  import { SdIcon } from '@sdcorejs/angular/modules/icon';
8
8
  import { FormsModule } from '@angular/forms';
9
9
  import * as pdfjsLib from 'pdfjs-dist';
10
10
 
11
11
  var _a$1;
12
+ // why: bộ đếm module-level để mỗi instance có id stage riêng — consumer có thể mount nhiều viewer
13
+ // trên cùng một trang và `aria-controls` của các chấm role="tab" phải trỏ đúng stage của mình.
14
+ let stageIdSeq = 0;
12
15
  class SdPreviewImage {
13
16
  // ==========================================
14
17
  // CONSTANTS
@@ -19,11 +22,18 @@ class SdPreviewImage {
19
22
  static MAX_ZOOM = 4;
20
23
  static ZOOM_STEP = 0.1;
21
24
  static SWIPE_THRESHOLD = 40;
25
+ // why: các chấm chỉ mục nay là role="tab" thật và cần aria-controls trỏ tới stage (role="tabpanel").
26
+ // Id phải duy nhất theo instance vì consumer có thể mount nhiều viewer trên cùng một trang.
27
+ stageId = `sd-preview-stage-${++stageIdSeq}`;
22
28
  // ==========================================
23
29
  // DI
24
30
  // ==========================================
25
31
  #hostEl = inject(ElementRef);
26
32
  #destroyRef = inject(DestroyRef);
33
+ // why: KHÔNG đọc biến global `document` — constructor chạy cả trên server khi SSR và sẽ
34
+ // ném `document is not defined`. Token DOCUMENT luôn resolve được (browser: document thật,
35
+ // server: document giả của platform-server) — cùng cách preview-pdf.browser.ts đang làm.
36
+ #document = inject(DOCUMENT);
27
37
  // ==========================================
28
38
  // INPUTS (signal-based, Angular 19 style)
29
39
  // ==========================================
@@ -132,11 +142,11 @@ class SdPreviewImage {
132
142
  // Đồng bộ #isFullscreen với trạng thái thực của browser (sự kiện 'fullscreenchange' fire
133
143
  // bất kể user thoát fullscreen bằng cách nào: nút Thoát, Esc, F11, programmatic).
134
144
  const onFullscreenChange = () => {
135
- this.#isFullscreen.set(document.fullscreenElement === this.#hostEl.nativeElement);
145
+ this.#isFullscreen.set(this.#document.fullscreenElement === this.#hostEl.nativeElement);
136
146
  };
137
- document.addEventListener('fullscreenchange', onFullscreenChange);
147
+ this.#document.addEventListener('fullscreenchange', onFullscreenChange);
138
148
  this.#destroyRef.onDestroy(() => {
139
- document.removeEventListener('fullscreenchange', onFullscreenChange);
149
+ this.#document.removeEventListener('fullscreenchange', onFullscreenChange);
140
150
  });
141
151
  // Reactive normalize: mỗi lần items() đổi → revoke blob cũ → load mới.
142
152
  // WHY effect không async: effect() callback không hỗ trợ async cleanup,
@@ -161,9 +171,9 @@ class SdPreviewImage {
161
171
  // Cleanup khi component bị destroy.
162
172
  this.#destroyRef.onDestroy(() => {
163
173
  this.#revokeAllBlobs();
164
- if (document.fullscreenElement === this.#hostEl.nativeElement) {
174
+ if (this.#document.fullscreenElement === this.#hostEl.nativeElement) {
165
175
  // Tránh để fullscreen "treo" khi component bị huỷ giữa chừng.
166
- document.exitFullscreen?.().catch(() => undefined);
176
+ this.#document.exitFullscreen?.().catch(() => undefined);
167
177
  }
168
178
  });
169
179
  }
@@ -228,10 +238,10 @@ class SdPreviewImage {
228
238
  // Ưu tiên CDN URL nếu có để tận dụng Content-Disposition của server,
229
239
  // fallback về blob URL khi user upload File trực tiếp.
230
240
  const href = img.url || img.blobUrl;
231
- const a = document.createElement('a');
241
+ const a = this.#document.createElement('a');
232
242
  a.href = href;
233
243
  a.download = img.name || 'image';
234
- document.body.appendChild(a);
244
+ this.#document.body.appendChild(a);
235
245
  a.click();
236
246
  a.remove();
237
247
  this.download.emit({ index: this.#activeIndex(), item: img });
@@ -239,11 +249,11 @@ class SdPreviewImage {
239
249
  toggleFullscreen() {
240
250
  // WHY: trạng thái #isFullscreen được sync qua sự kiện 'fullscreenchange' (xem constructor)
241
251
  // — single source of truth. Không cần .then(set(true/false)) ở đây nữa.
242
- if (!document.fullscreenElement) {
252
+ if (!this.#document.fullscreenElement) {
243
253
  this.#hostEl.nativeElement.requestFullscreen?.().catch(() => undefined);
244
254
  }
245
255
  else {
246
- document.exitFullscreen?.().catch(() => undefined);
256
+ this.#document.exitFullscreen?.().catch(() => undefined);
247
257
  }
248
258
  }
249
259
  /** Retry just the active image (Artboard G action). */
@@ -638,12 +648,12 @@ class SdPreviewImage {
638
648
  };
639
649
  }
640
650
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdPreviewImage, deps: [], target: i0.ɵɵFactoryTarget.Component });
641
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdPreviewImage, isStandalone: true, selector: "sd-preview-image", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, thumbnailPosition: { classPropertyName: "thumbnailPosition", publicName: "thumbnailPosition", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, downloadable: { classPropertyName: "downloadable", publicName: "downloadable", isSignal: true, isRequired: false, transformFunction: null }, zoomable: { classPropertyName: "zoomable", publicName: "zoomable", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, startIndex: { classPropertyName: "startIndex", publicName: "startIndex", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", activeIndexChange: "activeIndexChange", download: "download", imageError: "imageError" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.data-theme": "theme()", "class.sd-preview-image-host--fullscreen": "isFullscreen()" }, classAttribute: "sd-preview-image-host" }, ngImport: i0, template: "<!--\n Self-contained viewer shell. Component sizes to its parent (width:100%;\n height:100%) \u2014 consumer decides whether to wrap in <sd-modal>,\n <sd-side-drawer>, a page section, or use inline.\n-->\n<div [ngClass]=\"shellClass()\" role=\"region\">\n @let _stage = stage();\n @let _images = images();\n @let _thumbnailPosition = thumbnailPosition();\n @let _activeIndex = activeIndex();\n @let _activeImage = activeImage();\n\n <!-- ============================================\n HEADER BAR\n ============================================ -->\n @if (_stage !== 'empty' && _activeImage; as img) {\n <div class=\"sd-preview-header\">\n <div class=\"sd-preview-header__left\">\n <sd-icon class=\"sd-preview-header__icon\" fontIcon=\"image\"></sd-icon>\n <div class=\"sd-preview-header__meta\">\n <div class=\"sd-preview-header__name T14M\" [title]=\"img.name\">{{ img.name }}</div>\n @if (img.size) {\n <div class=\"sd-preview-header__sub\">\n {{ formatBytes(img.size) }}\n </div>\n }\n </div>\n </div>\n\n @if (_images.length > 1) {\n <div class=\"sd-preview-header__counter\" aria-live=\"polite\">\n {{ _activeIndex + 1 }} / {{ _images.length }}\n </div>\n }\n </div>\n } @else if (title()) {\n <!-- Optional override title even on empty state -->\n <div class=\"sd-preview-header sd-preview-header--simple\">\n <div class=\"sd-preview-header__name T14M\">{{ title() }}</div>\n </div>\n }\n\n <!-- ============================================\n BODY (stage + thumbnails per layout)\n ============================================ -->\n <div class=\"sd-preview-body\">\n\n <!-- STAGE ............................................................... -->\n <div\n class=\"sd-preview-stage\"\n [class.sd-preview-stage--dragging]=\"zoom() > 1\"\n (wheel)=\"onWheel($event)\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerCancel($event)\"\n (dblclick)=\"onImageDblClick()\">\n\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--muted\">\n <sd-icon fontIcon=\"image_not_supported\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.no-image' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.no-image-hint' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n }\n @default {\n @if (_activeImage; as img) {\n @if (img.loading) {\n <!-- Per-image loading (retry inline) \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-loading\" role=\"status\"\n [attr.aria-label]=\"'core.component.preview-image.loading' | translate\">\n <div class=\"sd-preview-spinner\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n } @else if (img.error) {\n <!-- Inline broken-image placeholder \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-broken\" role=\"img\"\n [attr.aria-label]=\"'core.component.preview-image.load-error' | translate\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--danger\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.load-error' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.load-error-hint' | translate }}\n </div>\n <button type=\"button\" class=\"sd-preview-btn-primary\" (click)=\"retryActive()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-image.retry' | translate }}</span>\n </button>\n </div>\n } @else {\n <img\n class=\"sd-preview-image\"\n [src]=\"img.blobUrl || img.url\"\n [alt]=\"img.alt || img.name\"\n [style.transform]=\"imageTransform()\"\n (error)=\"onImageError()\"\n draggable=\"false\" />\n\n @if (img.caption) {\n <div class=\"sd-preview-caption\">{{ img.caption }}</div>\n }\n }\n }\n }\n }\n\n <!-- KEYBOARD HINT ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-kbd-hint\" aria-hidden=\"true\">\n <span class=\"sd-preview-kbd\">\u2190</span>\n <span class=\"sd-preview-kbd\">\u2192</span>\n <span class=\"sd-preview-kbd-text\">{{ 'core.component.preview-image.kbd-hint-nav' | translate }}</span>\n </div>\n }\n\n <!-- NAV ARROWS ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--prev\"\n [disabled]=\"!hasPrev()\"\n [attr.aria-label]=\"'core.component.preview-image.prev' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"updateCurrentImage(-1)\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--next\"\n [disabled]=\"!hasNext()\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"updateCurrentImage(1)\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n }\n\n <!-- DOTS INDICATOR ......................................... -->\n @if (_thumbnailPosition === 'dots' && _stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-dots\" role=\"tablist\">\n @for (img of _images; track img.id; let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-dot\"\n [class.sd-preview-dot--active]=\"i === _activeIndex\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdDot(i)\"\n (click)=\"onClickThumbnailImage(i)\"></button>\n }\n </div>\n }\n\n <!-- FLOATING TOOLBAR ....................................... -->\n @if (showToolbar() && _stage === 'ready') {\n <div class=\"sd-preview-toolbar\" role=\"toolbar\">\n @if (zoomable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"zoom_out\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__readout\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"zoom_in\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn sd-preview-toolbar__btn--with-label\"\n [attr.aria-label]=\"'core.component.preview-image.fit' | translate\"\n [attr.data-autoId]=\"autoIdFit()\"\n (click)=\"fitToScreen()\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span class=\"sd-preview-toolbar__btn-label\">{{ 'core.component.preview-image.fit' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.rotate' | translate\"\n [attr.title]=\"'core.component.preview-image.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.download' | translate\"\n [attr.title]=\"'core.component.preview-image.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadCurrent()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n </div>\n }\n </div>\n\n <!-- THUMBNAIL STRIP .................................................... -->\n @if (\n _stage === 'ready' &&\n _images.length > 1 &&\n _thumbnailPosition !== 'none' &&\n _thumbnailPosition !== 'dots'\n ) {\n <div\n class=\"sd-preview-thumbs\"\n [class.sd-preview-thumbs--vertical]=\"_thumbnailPosition === 'left' || _thumbnailPosition === 'right'\">\n @for (img of _images; track trackByImage($index, img); let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-thumb\"\n [class.sd-preview-thumb--active]=\"i === _activeIndex\"\n [id]=\"'sd-preview-thumb-' + i\"\n [attr.aria-label]=\"img.name\"\n [attr.aria-current]=\"i === _activeIndex ? 'true' : null\"\n [attr.data-autoId]=\"autoIdThumb(i)\"\n (click)=\"onClickThumbnailImage(i)\">\n @if (img.error) {\n <div class=\"sd-preview-thumb__broken\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n } @else {\n <img class=\"sd-preview-thumb__img\" [src]=\"img.blobUrl || img.url\" [alt]=\"img.alt || img.name\" />\n }\n <span class=\"sd-preview-thumb__badge\">{{ i + 1 }}</span>\n @if (_thumbnailPosition === 'right' || _thumbnailPosition === 'left') {\n <span class=\"sd-preview-thumb__name\" [title]=\"img.name\">{{ img.name }}</span>\n }\n </button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [":host{--sd-preview-shell-bg: #0d0e10;--sd-preview-shell-bg-2: #17191c;--sd-preview-panel-bg: rgba(15, 17, 20, .92);--sd-preview-divider: rgba(255, 255, 255, .08);--sd-preview-fg: #f5f6f7;--sd-preview-fg-muted: rgba(255, 255, 255, .6);--sd-preview-fg-subtle: rgba(255, 255, 255, .4);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .18));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .1);--sd-preview-danger-border: rgba(186, 26, 26, .3);--sd-preview-hover-bg: rgba(255, 255, 255, .1);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-preview-shell-bg: #f7f8fa;--sd-preview-shell-bg-2: #ffffff;--sd-preview-panel-bg: rgba(255, 255, 255, .94);--sd-preview-divider: rgba(0, 0, 0, .08);--sd-preview-fg: #1a1c1f;--sd-preview-fg-muted: rgba(0, 0, 0, .62);--sd-preview-fg-subtle: rgba(0, 0, 0, .42);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .16));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .08);--sd-preview-danger-border: rgba(186, 26, 26, .25);--sd-preview-hover-bg: rgba(0, 0, 0, .06)}.sd-preview-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-preview-shell-bg);color:var(--sd-preview-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-shell--fullscreen{height:100vh;width:100vw;border-radius:0}:host(.sd-preview-image-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-header{position:relative;display:flex;align-items:center;height:68px;padding:0 16px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-preview-divider);flex:0 0 auto;z-index:2}.sd-preview-header--simple{justify-content:space-between}.sd-preview-header__left{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto}.sd-preview-header__icon{color:var(--sd-preview-fg-muted);font-size:20px;width:20px;height:20px}.sd-preview-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-header__name{color:var(--sd-preview-fg);max-width:360px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-header__sub{font-size:12px;color:var(--sd-preview-fg-muted);margin-top:2px}.sd-preview-header__counter{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:13px;font-weight:500;color:var(--sd-preview-fg-muted);background:#ffffff14;padding:4px 12px;border-radius:999px;font-variant-numeric:tabular-nums}.sd-preview-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}.sd-preview-shell--pos-bottom .sd-preview-body{flex-direction:column}.sd-preview-shell--pos-top .sd-preview-body{flex-direction:column-reverse}.sd-preview-shell--pos-right .sd-preview-body{flex-direction:row}.sd-preview-shell--pos-left .sd-preview-body{flex-direction:row-reverse}.sd-preview-shell--pos-dots .sd-preview-body,.sd-preview-shell--pos-none .sd-preview-body{flex-direction:column}.sd-preview-stage{position:relative;flex:1 1 auto;min-height:0;min-width:0;background:var(--sd-preview-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.02) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.02) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;display:flex;align-items:center;justify-content:center;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.sd-preview-stage--dragging{cursor:grab}:host([data-theme=light]) .sd-preview-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-image{max-width:100%;max-height:100%;object-fit:contain;transform-origin:center center;transition:transform .2s ease;will-change:transform;pointer-events:none}.sd-preview-caption{position:absolute;bottom:96px;left:50%;transform:translate(-50%);max-width:70%;padding:6px 12px;background:#0f1114b3;color:var(--sd-preview-fg);border-radius:8px;font-size:13px;text-align:center}.sd-preview-image-broken,.sd-preview-image-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;width:100%;max-width:480px;padding:24px;text-align:center}.sd-preview-nav{position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;border-radius:999px;border:none;background:#14141699;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--sd-preview-fg);display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 16px #0000004d;transition:background .12s;z-index:3}.sd-preview-nav sd-icon{font-size:28px;width:28px;height:28px}.sd-preview-nav:hover:not(:disabled){background:#ffffff2e}.sd-preview-nav:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-nav:disabled{background:#ffffff0a;opacity:.35;cursor:not-allowed}.sd-preview-nav--prev{left:16px}.sd-preview-nav--next{right:16px}.sd-preview-toolbar{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:4px;padding:6px 8px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-preview-divider);border-radius:12px;box-shadow:0 8px 30px #0006;z-index:3}.sd-preview-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:36px;height:36px;padding:0 8px;background:transparent;border:none;color:var(--sd-preview-fg);border-radius:8px;cursor:pointer;transition:background .12s}.sd-preview-toolbar__btn sd-icon{font-size:20px;width:20px;height:20px}.sd-preview-toolbar__btn--with-label{padding:0 12px}.sd-preview-toolbar__btn:hover:not(:disabled){background:var(--sd-preview-hover-bg)}.sd-preview-toolbar__btn:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-toolbar__btn-label{font-size:13px;font-weight:500}.sd-preview-toolbar__readout{min-width:56px;text-align:center;color:var(--sd-preview-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums}.sd-preview-toolbar__divider{width:1px;height:20px;background:var(--sd-preview-divider);margin:0 4px}.sd-preview-thumbs{display:flex;gap:8px;padding:12px;background:var(--sd-preview-shell-bg-2);overflow:auto;flex:0 0 auto;flex-direction:row;height:116px}.sd-preview-thumbs--vertical{flex-direction:column;width:152px;height:100%}.sd-preview-thumb{position:relative;flex:0 0 auto;width:120px;height:76px;padding:0;border:none;background:transparent;border-radius:8px;outline:1px solid var(--sd-preview-divider);outline-offset:-1px;opacity:.7;overflow:hidden;cursor:pointer;transition:opacity .12s,outline-color .12s}.sd-preview-thumb:hover{opacity:1}.sd-preview-thumb--active{opacity:1;outline:2px solid var(--sd-preview-primary);outline-offset:-2px;box-shadow:inset 0 0 0 2px var(--sd-preview-primary)}.sd-preview-thumb:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-thumb__img{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}.sd-preview-thumb__broken{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--sd-preview-danger-soft);color:var(--sd-preview-danger);pointer-events:none}.sd-preview-thumb__broken sd-icon{font-size:24px;width:24px;height:24px}.sd-preview-thumb__badge{position:absolute;top:4px;right:4px;background:#000000b3;color:#fff;font-size:10px;font-weight:600;padding:1px 5px;border-radius:4px;pointer-events:none}.sd-preview-thumb__name{position:absolute;left:0;right:0;bottom:0;padding:14px 6px 4px;color:var(--sd-preview-fg);font-size:11px;line-height:1.3;background:linear-gradient(to top,rgba(0,0,0,.85),transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none}.sd-preview-thumbs--vertical .sd-preview-thumb{width:128px}.sd-preview-dots{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:6px;z-index:3}.sd-preview-dot{width:8px;height:8px;border:none;background:#ffffff4d;border-radius:999px;padding:0;cursor:pointer;transition:width .18s,background .18s}.sd-preview-dot--active{width:24px;background:#f5f6f7}.sd-preview-dot:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-status{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:24px}.sd-preview-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-preview-divider);background:#ffffff0d}.sd-preview-status__tile sd-icon{font-size:48px;width:48px;height:48px;color:var(--sd-preview-fg-subtle)}.sd-preview-status__tile--muted{background:#ffffff0d;border-color:var(--sd-preview-divider)}.sd-preview-status__tile--danger{background:var(--sd-preview-danger-soft);border-color:var(--sd-preview-danger-border)}.sd-preview-status__tile--danger sd-icon{color:var(--sd-preview-danger)}.sd-preview-status__title{font-size:16px;font-weight:500;color:var(--sd-preview-fg)}.sd-preview-status__hint{font-size:13px;color:var(--sd-preview-fg-muted);max-width:320px}.sd-preview-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-preview-primary);animation:sd-preview-spin .9s linear infinite}@keyframes sd-preview-spin{to{transform:rotate(360deg)}}.sd-preview-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-preview-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-btn-primary:hover{filter:brightness(1.1)}.sd-preview-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-kbd-hint{position:absolute;top:16px;left:16px;display:flex;align-items:center;gap:6px;z-index:2;pointer-events:none}.sd-preview-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 5px;border:1px solid var(--sd-preview-divider);background:#ffffff0a;color:var(--sd-preview-fg-muted);border-radius:4px;font:500 11px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-kbd-text,.sd-preview-kbd-dot{font-size:11px;color:var(--sd-preview-fg-subtle)}.sd-preview-kbd-dot{margin:0 2px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
651
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdPreviewImage, isStandalone: true, selector: "sd-preview-image", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, thumbnailPosition: { classPropertyName: "thumbnailPosition", publicName: "thumbnailPosition", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, downloadable: { classPropertyName: "downloadable", publicName: "downloadable", isSignal: true, isRequired: false, transformFunction: null }, zoomable: { classPropertyName: "zoomable", publicName: "zoomable", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, startIndex: { classPropertyName: "startIndex", publicName: "startIndex", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", activeIndexChange: "activeIndexChange", download: "download", imageError: "imageError" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.data-theme": "theme()", "class.sd-preview-image-host--fullscreen": "isFullscreen()" }, classAttribute: "sd-preview-image-host" }, ngImport: i0, template: "<!--\n Self-contained viewer shell. Component sizes to its parent (width:100%;\n height:100%) \u2014 consumer decides whether to wrap in <sd-modal>,\n <sd-side-drawer>, a page section, or use inline.\n-->\n<div [ngClass]=\"shellClass()\" role=\"region\">\n @let _stage = stage();\n @let _images = images();\n @let _thumbnailPosition = thumbnailPosition();\n @let _activeIndex = activeIndex();\n @let _activeImage = activeImage();\n\n <!-- ============================================\n HEADER BAR\n ============================================ -->\n @if (_stage !== 'empty' && _activeImage; as img) {\n <div class=\"sd-preview-header\">\n <div class=\"sd-preview-header__left\">\n <sd-icon class=\"sd-preview-header__icon\" fontIcon=\"image\"></sd-icon>\n <div class=\"sd-preview-header__meta\">\n <div class=\"sd-preview-header__name T14M\" [title]=\"img.name\">{{ img.name }}</div>\n @if (img.size) {\n <div class=\"sd-preview-header__sub\">\n {{ formatBytes(img.size) }}\n </div>\n }\n </div>\n </div>\n\n @if (_images.length > 1) {\n <div class=\"sd-preview-header__counter\" aria-live=\"polite\">\n {{ _activeIndex + 1 }} / {{ _images.length }}\n </div>\n }\n </div>\n } @else if (title()) {\n <!-- Optional override title even on empty state -->\n <div class=\"sd-preview-header sd-preview-header--simple\">\n <div class=\"sd-preview-header__name T14M\">{{ title() }}</div>\n </div>\n }\n\n <!-- ============================================\n BODY (stage + thumbnails per layout)\n ============================================ -->\n <div class=\"sd-preview-body\">\n\n <!-- STAGE ............................................................... -->\n <div\n class=\"sd-preview-stage\"\n [id]=\"stageId\"\n role=\"tabpanel\"\n [class.sd-preview-stage--dragging]=\"zoom() > 1\"\n (wheel)=\"onWheel($event)\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerCancel($event)\"\n (dblclick)=\"onImageDblClick()\">\n\n @switch (_stage) {\n <!-- why: c\u1EA3 hai tr\u1EA1ng th\u00E1i n\u00E0y \u0111\u1EC1u xu\u1EA5t hi\u1EC7n B\u1EA4T \u0110\u1ED2NG B\u1ED8 (sau khi normalize/t\u1EA3i \u1EA3nh) n\u00EAn\n c\u1EA7n live region, n\u1EBFu kh\u00F4ng screen reader kh\u00F4ng bi\u1EBFt viewer \u0111\u00E3 r\u1ED7ng hay \u0111ang t\u1EA3i. -->\n @case ('empty') {\n <div class=\"sd-preview-status\" role=\"status\" aria-live=\"polite\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--muted\">\n <sd-icon fontIcon=\"image_not_supported\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.no-image' | sdTranslate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.no-image-hint' | sdTranslate }}\n </div>\n </div>\n }\n @case ('loading') {\n <div class=\"sd-preview-status\" role=\"status\" aria-live=\"polite\">\n <div class=\"sd-preview-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | sdTranslate }}\n </div>\n </div>\n }\n @default {\n @if (_activeImage; as img) {\n @if (img.loading) {\n <!-- Per-image loading (retry inline) \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-loading\" role=\"status\"\n [attr.aria-label]=\"'core.component.preview-image.loading' | sdTranslate\">\n <div class=\"sd-preview-spinner\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | sdTranslate }}\n </div>\n </div>\n } @else if (img.error) {\n <!-- Inline broken-image placeholder \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-broken\" role=\"img\"\n [attr.aria-label]=\"'core.component.preview-image.load-error' | sdTranslate\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--danger\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.load-error' | sdTranslate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.load-error-hint' | sdTranslate }}\n </div>\n <button type=\"button\" class=\"sd-preview-btn-primary\" (click)=\"retryActive()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-image.retry' | sdTranslate }}</span>\n </button>\n </div>\n } @else {\n <img\n class=\"sd-preview-image\"\n [src]=\"img.blobUrl || img.url\"\n [alt]=\"img.alt || img.name\"\n [style.transform]=\"imageTransform()\"\n (error)=\"onImageError()\"\n draggable=\"false\" />\n\n @if (img.caption) {\n <div class=\"sd-preview-caption\">{{ img.caption }}</div>\n }\n }\n }\n }\n }\n\n <!-- KEYBOARD HINT ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-kbd-hint\" aria-hidden=\"true\">\n <span class=\"sd-preview-kbd\">\u2190</span>\n <span class=\"sd-preview-kbd\">\u2192</span>\n <span class=\"sd-preview-kbd-text\">{{ 'core.component.preview-image.kbd-hint-nav' | sdTranslate }}</span>\n </div>\n }\n\n <!-- NAV ARROWS ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--prev\"\n [disabled]=\"!hasPrev()\"\n [attr.aria-label]=\"'core.component.preview-image.prev' | sdTranslate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"updateCurrentImage(-1)\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--next\"\n [disabled]=\"!hasNext()\"\n [attr.aria-label]=\"'core.component.preview-image.next' | sdTranslate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"updateCurrentImage(1)\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n }\n\n <!-- DOTS INDICATOR ......................................... -->\n <!-- why: role=\"tablist\" \u1EDF \u0111\u00E2y l\u00E0 ARIA SAI c\u1EA5u tr\u00FAc \u2014 con c\u1EE7a n\u00F3 l\u00E0 <button> tr\u1EA7n, kh\u00F4ng c\u00F3\n role=\"tab\", kh\u00F4ng aria-selected, v\u00E0 c\u0169ng kh\u00F4ng c\u00F3 tabpanel n\u00E0o \u0111\u1EC3 tr\u1ECF t\u1EDBi. M\u1ED9t tablist\n r\u1ED7ng tab khi\u1EBFn screen reader th\u00F4ng b\u00E1o \"danh s\u00E1ch tab, 0 tab\". C\u00E1c ch\u1EA5m n\u00E0y th\u1EF1c ch\u1EA5t l\u00E0\n b\u1ED9 ch\u1ECDn \u1EA3nh, n\u00EAn ho\u00E0n thi\u1EC7n pattern: tablist + tab + aria-selected + aria-controls tr\u1ECF\n v\u1EC1 v\u00F9ng stage. Nh\u00E3n c\u0169ng s\u1EEDa: tr\u01B0\u1EDBc \u0111\u00E2y M\u1ECCI ch\u1EA5m \u0111\u1EC1u \u0111\u1ECDc l\u00E0 \"\u1EA2nh ti\u1EBFp theo\". -->\n @if (_thumbnailPosition === 'dots' && _stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-dots\" role=\"tablist\" [attr.aria-label]=\"'core.component.preview-image.title' | sdTranslate\">\n @for (img of _images; track img.id; let i = $index) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"sd-preview-dot\"\n [class.sd-preview-dot--active]=\"i === _activeIndex\"\n [attr.aria-selected]=\"i === _activeIndex\"\n [attr.aria-controls]=\"stageId\"\n [attr.tabindex]=\"i === _activeIndex ? 0 : -1\"\n [attr.aria-label]=\"img.alt || img.name\"\n [attr.data-autoId]=\"autoIdDot(i)\"\n (click)=\"onClickThumbnailImage(i)\"></button>\n }\n </div>\n }\n\n <!-- FLOATING TOOLBAR ....................................... -->\n @if (showToolbar() && _stage === 'ready') {\n <div class=\"sd-preview-toolbar\" role=\"toolbar\">\n @if (zoomable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-out' | sdTranslate\"\n [attr.title]=\"'core.component.preview-image.zoom-out' | sdTranslate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"zoom_out\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__readout\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-in' | sdTranslate\"\n [attr.title]=\"'core.component.preview-image.zoom-in' | sdTranslate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"zoom_in\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn sd-preview-toolbar__btn--with-label\"\n [attr.aria-label]=\"'core.component.preview-image.fit' | sdTranslate\"\n [attr.data-autoId]=\"autoIdFit()\"\n (click)=\"fitToScreen()\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span class=\"sd-preview-toolbar__btn-label\">{{ 'core.component.preview-image.fit' | sdTranslate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.rotate' | sdTranslate\"\n [attr.title]=\"'core.component.preview-image.rotate' | sdTranslate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.download' | sdTranslate\"\n [attr.title]=\"'core.component.preview-image.download' | sdTranslate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadCurrent()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.fullscreen' | sdTranslate\"\n [attr.title]=\"'core.component.preview-image.fullscreen' | sdTranslate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n </div>\n }\n </div>\n\n <!-- THUMBNAIL STRIP .................................................... -->\n @if (\n _stage === 'ready' &&\n _images.length > 1 &&\n _thumbnailPosition !== 'none' &&\n _thumbnailPosition !== 'dots'\n ) {\n <div\n class=\"sd-preview-thumbs\"\n [class.sd-preview-thumbs--vertical]=\"_thumbnailPosition === 'left' || _thumbnailPosition === 'right'\">\n @for (img of _images; track trackByImage($index, img); let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-thumb\"\n [class.sd-preview-thumb--active]=\"i === _activeIndex\"\n [id]=\"'sd-preview-thumb-' + i\"\n [attr.aria-label]=\"img.name\"\n [attr.aria-current]=\"i === _activeIndex ? 'true' : null\"\n [attr.data-autoId]=\"autoIdThumb(i)\"\n (click)=\"onClickThumbnailImage(i)\">\n @if (img.error) {\n <div class=\"sd-preview-thumb__broken\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n } @else {\n <img class=\"sd-preview-thumb__img\" [src]=\"img.blobUrl || img.url\" [alt]=\"img.alt || img.name\" />\n }\n <span class=\"sd-preview-thumb__badge\">{{ i + 1 }}</span>\n @if (_thumbnailPosition === 'right' || _thumbnailPosition === 'left') {\n <span class=\"sd-preview-thumb__name\" [title]=\"img.name\">{{ img.name }}</span>\n }\n </button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [":host{--sd-preview-shell-bg: #0d0e10;--sd-preview-shell-bg-2: #17191c;--sd-preview-panel-bg: rgba(15, 17, 20, .92);--sd-preview-divider: rgba(255, 255, 255, .08);--sd-preview-fg: #f5f6f7;--sd-preview-fg-muted: rgba(255, 255, 255, .6);--sd-preview-fg-subtle: rgba(255, 255, 255, .4);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .18));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .1);--sd-preview-danger-border: rgba(186, 26, 26, .3);--sd-preview-hover-bg: rgba(255, 255, 255, .1);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-preview-shell-bg: #f7f8fa;--sd-preview-shell-bg-2: #ffffff;--sd-preview-panel-bg: rgba(255, 255, 255, .94);--sd-preview-divider: rgba(0, 0, 0, .08);--sd-preview-fg: #1a1c1f;--sd-preview-fg-muted: rgba(0, 0, 0, .62);--sd-preview-fg-subtle: rgba(0, 0, 0, .42);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .16));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .08);--sd-preview-danger-border: rgba(186, 26, 26, .25);--sd-preview-hover-bg: rgba(0, 0, 0, .06)}.sd-preview-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-preview-shell-bg);color:var(--sd-preview-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-shell--fullscreen{height:100vh;width:100vw;border-radius:0}:host(.sd-preview-image-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-header{position:relative;display:flex;align-items:center;height:68px;padding:0 16px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-preview-divider);flex:0 0 auto;z-index:2}.sd-preview-header--simple{justify-content:space-between}.sd-preview-header__left{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto}.sd-preview-header__icon{color:var(--sd-preview-fg-muted);font-size:20px;width:20px;height:20px}.sd-preview-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-header__name{color:var(--sd-preview-fg);max-width:360px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-header__sub{font-size:12px;color:var(--sd-preview-fg-muted);margin-top:2px}.sd-preview-header__counter{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:13px;font-weight:500;color:var(--sd-preview-fg-muted);background:#ffffff14;padding:4px 12px;border-radius:999px;font-variant-numeric:tabular-nums}.sd-preview-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}.sd-preview-shell--pos-bottom .sd-preview-body{flex-direction:column}.sd-preview-shell--pos-top .sd-preview-body{flex-direction:column-reverse}.sd-preview-shell--pos-right .sd-preview-body{flex-direction:row}.sd-preview-shell--pos-left .sd-preview-body{flex-direction:row-reverse}.sd-preview-shell--pos-dots .sd-preview-body,.sd-preview-shell--pos-none .sd-preview-body{flex-direction:column}.sd-preview-stage{position:relative;flex:1 1 auto;min-height:0;min-width:0;background:var(--sd-preview-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.02) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.02) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;display:flex;align-items:center;justify-content:center;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.sd-preview-stage--dragging{cursor:grab}:host([data-theme=light]) .sd-preview-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-image{max-width:100%;max-height:100%;object-fit:contain;transform-origin:center center;transition:transform .2s ease;will-change:transform;pointer-events:none}.sd-preview-caption{position:absolute;bottom:96px;left:50%;transform:translate(-50%);max-width:70%;padding:6px 12px;background:#0f1114b3;color:var(--sd-preview-fg);border-radius:8px;font-size:13px;text-align:center}.sd-preview-image-broken,.sd-preview-image-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;width:100%;max-width:480px;padding:24px;text-align:center}.sd-preview-nav{position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;border-radius:999px;border:none;background:#14141699;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--sd-preview-fg);display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 16px #0000004d;transition:background .12s;z-index:3}.sd-preview-nav sd-icon{font-size:28px;width:28px;height:28px}.sd-preview-nav:hover:not(:disabled){background:#ffffff2e}.sd-preview-nav:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-nav:disabled{background:#ffffff0a;opacity:.35;cursor:not-allowed}.sd-preview-nav--prev{left:16px}.sd-preview-nav--next{right:16px}.sd-preview-toolbar{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:4px;padding:6px 8px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-preview-divider);border-radius:12px;box-shadow:0 8px 30px #0006;z-index:3}.sd-preview-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:36px;height:36px;padding:0 8px;background:transparent;border:none;color:var(--sd-preview-fg);border-radius:8px;cursor:pointer;transition:background .12s}.sd-preview-toolbar__btn sd-icon{font-size:20px;width:20px;height:20px}.sd-preview-toolbar__btn--with-label{padding:0 12px}.sd-preview-toolbar__btn:hover:not(:disabled){background:var(--sd-preview-hover-bg)}.sd-preview-toolbar__btn:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-toolbar__btn-label{font-size:13px;font-weight:500}.sd-preview-toolbar__readout{min-width:56px;text-align:center;color:var(--sd-preview-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums}.sd-preview-toolbar__divider{width:1px;height:20px;background:var(--sd-preview-divider);margin:0 4px}.sd-preview-thumbs{display:flex;gap:8px;padding:12px;background:var(--sd-preview-shell-bg-2);overflow:auto;flex:0 0 auto;flex-direction:row;height:116px}.sd-preview-thumbs--vertical{flex-direction:column;width:152px;height:100%}.sd-preview-thumb{position:relative;flex:0 0 auto;width:120px;height:76px;padding:0;border:none;background:transparent;border-radius:8px;outline:1px solid var(--sd-preview-divider);outline-offset:-1px;opacity:.7;overflow:hidden;cursor:pointer;transition:opacity .12s,outline-color .12s}.sd-preview-thumb:hover{opacity:1}.sd-preview-thumb--active{opacity:1;outline:2px solid var(--sd-preview-primary);outline-offset:-2px;box-shadow:inset 0 0 0 2px var(--sd-preview-primary)}.sd-preview-thumb:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-thumb__img{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}.sd-preview-thumb__broken{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--sd-preview-danger-soft);color:var(--sd-preview-danger);pointer-events:none}.sd-preview-thumb__broken sd-icon{font-size:24px;width:24px;height:24px}.sd-preview-thumb__badge{position:absolute;top:4px;right:4px;background:#000000b3;color:#fff;font-size:10px;font-weight:600;padding:1px 5px;border-radius:4px;pointer-events:none}.sd-preview-thumb__name{position:absolute;left:0;right:0;bottom:0;padding:14px 6px 4px;color:var(--sd-preview-fg);font-size:11px;line-height:1.3;background:linear-gradient(to top,rgba(0,0,0,.85),transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none}.sd-preview-thumbs--vertical .sd-preview-thumb{width:128px}.sd-preview-dots{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:6px;z-index:3}.sd-preview-dot{width:8px;height:8px;border:none;background:#ffffff4d;border-radius:999px;padding:0;cursor:pointer;transition:width .18s,background .18s}.sd-preview-dot--active{width:24px;background:#f5f6f7}.sd-preview-dot:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-status{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:24px}.sd-preview-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-preview-divider);background:#ffffff0d}.sd-preview-status__tile sd-icon{font-size:48px;width:48px;height:48px;color:var(--sd-preview-fg-subtle)}.sd-preview-status__tile--muted{background:#ffffff0d;border-color:var(--sd-preview-divider)}.sd-preview-status__tile--danger{background:var(--sd-preview-danger-soft);border-color:var(--sd-preview-danger-border)}.sd-preview-status__tile--danger sd-icon{color:var(--sd-preview-danger)}.sd-preview-status__title{font-size:16px;font-weight:500;color:var(--sd-preview-fg)}.sd-preview-status__hint{font-size:13px;color:var(--sd-preview-fg-muted);max-width:320px}.sd-preview-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-preview-primary);animation:sd-preview-spin .9s linear infinite}@keyframes sd-preview-spin{to{transform:rotate(360deg)}}.sd-preview-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-preview-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-btn-primary:hover{filter:brightness(1.1)}.sd-preview-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-kbd-hint{position:absolute;top:16px;left:16px;display:flex;align-items:center;gap:6px;z-index:2;pointer-events:none}.sd-preview-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 5px;border:1px solid var(--sd-preview-divider);background:#ffffff0a;color:var(--sd-preview-fg-muted);border-radius:4px;font:500 11px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-kbd-text,.sd-preview-kbd-dot{font-size:11px;color:var(--sd-preview-fg-subtle)}.sd-preview-kbd-dot{margin:0 2px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: SdTranslatePipe, name: "sdTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
642
652
  }
643
653
  _a$1 = SdPreviewImage;
644
654
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdPreviewImage, decorators: [{
645
655
  type: Component,
646
- args: [{ selector: 'sd-preview-image', standalone: true, imports: [SdIcon, CommonModule, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
656
+ args: [{ selector: 'sd-preview-image', standalone: true, imports: [SdIcon, CommonModule, SdTranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
647
657
  tabindex: '0',
648
658
  class: 'sd-preview-image-host',
649
659
  // WHY data-theme attribute (not class): SCSS uses :host([data-theme="light"])
@@ -651,7 +661,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
651
661
  // visible in devtools without polluting the class list.
652
662
  '[attr.data-theme]': 'theme()',
653
663
  '[class.sd-preview-image-host--fullscreen]': 'isFullscreen()',
654
- }, template: "<!--\n Self-contained viewer shell. Component sizes to its parent (width:100%;\n height:100%) \u2014 consumer decides whether to wrap in <sd-modal>,\n <sd-side-drawer>, a page section, or use inline.\n-->\n<div [ngClass]=\"shellClass()\" role=\"region\">\n @let _stage = stage();\n @let _images = images();\n @let _thumbnailPosition = thumbnailPosition();\n @let _activeIndex = activeIndex();\n @let _activeImage = activeImage();\n\n <!-- ============================================\n HEADER BAR\n ============================================ -->\n @if (_stage !== 'empty' && _activeImage; as img) {\n <div class=\"sd-preview-header\">\n <div class=\"sd-preview-header__left\">\n <sd-icon class=\"sd-preview-header__icon\" fontIcon=\"image\"></sd-icon>\n <div class=\"sd-preview-header__meta\">\n <div class=\"sd-preview-header__name T14M\" [title]=\"img.name\">{{ img.name }}</div>\n @if (img.size) {\n <div class=\"sd-preview-header__sub\">\n {{ formatBytes(img.size) }}\n </div>\n }\n </div>\n </div>\n\n @if (_images.length > 1) {\n <div class=\"sd-preview-header__counter\" aria-live=\"polite\">\n {{ _activeIndex + 1 }} / {{ _images.length }}\n </div>\n }\n </div>\n } @else if (title()) {\n <!-- Optional override title even on empty state -->\n <div class=\"sd-preview-header sd-preview-header--simple\">\n <div class=\"sd-preview-header__name T14M\">{{ title() }}</div>\n </div>\n }\n\n <!-- ============================================\n BODY (stage + thumbnails per layout)\n ============================================ -->\n <div class=\"sd-preview-body\">\n\n <!-- STAGE ............................................................... -->\n <div\n class=\"sd-preview-stage\"\n [class.sd-preview-stage--dragging]=\"zoom() > 1\"\n (wheel)=\"onWheel($event)\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerCancel($event)\"\n (dblclick)=\"onImageDblClick()\">\n\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--muted\">\n <sd-icon fontIcon=\"image_not_supported\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.no-image' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.no-image-hint' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n <div class=\"sd-preview-status\">\n <div class=\"sd-preview-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n }\n @default {\n @if (_activeImage; as img) {\n @if (img.loading) {\n <!-- Per-image loading (retry inline) \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-loading\" role=\"status\"\n [attr.aria-label]=\"'core.component.preview-image.loading' | translate\">\n <div class=\"sd-preview-spinner\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | translate }}\n </div>\n </div>\n } @else if (img.error) {\n <!-- Inline broken-image placeholder \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-broken\" role=\"img\"\n [attr.aria-label]=\"'core.component.preview-image.load-error' | translate\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--danger\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.load-error' | translate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.load-error-hint' | translate }}\n </div>\n <button type=\"button\" class=\"sd-preview-btn-primary\" (click)=\"retryActive()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-image.retry' | translate }}</span>\n </button>\n </div>\n } @else {\n <img\n class=\"sd-preview-image\"\n [src]=\"img.blobUrl || img.url\"\n [alt]=\"img.alt || img.name\"\n [style.transform]=\"imageTransform()\"\n (error)=\"onImageError()\"\n draggable=\"false\" />\n\n @if (img.caption) {\n <div class=\"sd-preview-caption\">{{ img.caption }}</div>\n }\n }\n }\n }\n }\n\n <!-- KEYBOARD HINT ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-kbd-hint\" aria-hidden=\"true\">\n <span class=\"sd-preview-kbd\">\u2190</span>\n <span class=\"sd-preview-kbd\">\u2192</span>\n <span class=\"sd-preview-kbd-text\">{{ 'core.component.preview-image.kbd-hint-nav' | translate }}</span>\n </div>\n }\n\n <!-- NAV ARROWS ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--prev\"\n [disabled]=\"!hasPrev()\"\n [attr.aria-label]=\"'core.component.preview-image.prev' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"updateCurrentImage(-1)\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--next\"\n [disabled]=\"!hasNext()\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"updateCurrentImage(1)\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n }\n\n <!-- DOTS INDICATOR ......................................... -->\n @if (_thumbnailPosition === 'dots' && _stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-dots\" role=\"tablist\">\n @for (img of _images; track img.id; let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-dot\"\n [class.sd-preview-dot--active]=\"i === _activeIndex\"\n [attr.aria-label]=\"'core.component.preview-image.next' | translate\"\n [attr.data-autoId]=\"autoIdDot(i)\"\n (click)=\"onClickThumbnailImage(i)\"></button>\n }\n </div>\n }\n\n <!-- FLOATING TOOLBAR ....................................... -->\n @if (showToolbar() && _stage === 'ready') {\n <div class=\"sd-preview-toolbar\" role=\"toolbar\">\n @if (zoomable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"zoom_out\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__readout\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-image.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"zoom_in\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn sd-preview-toolbar__btn--with-label\"\n [attr.aria-label]=\"'core.component.preview-image.fit' | translate\"\n [attr.data-autoId]=\"autoIdFit()\"\n (click)=\"fitToScreen()\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span class=\"sd-preview-toolbar__btn-label\">{{ 'core.component.preview-image.fit' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.rotate' | translate\"\n [attr.title]=\"'core.component.preview-image.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.download' | translate\"\n [attr.title]=\"'core.component.preview-image.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadCurrent()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-image.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n </div>\n }\n </div>\n\n <!-- THUMBNAIL STRIP .................................................... -->\n @if (\n _stage === 'ready' &&\n _images.length > 1 &&\n _thumbnailPosition !== 'none' &&\n _thumbnailPosition !== 'dots'\n ) {\n <div\n class=\"sd-preview-thumbs\"\n [class.sd-preview-thumbs--vertical]=\"_thumbnailPosition === 'left' || _thumbnailPosition === 'right'\">\n @for (img of _images; track trackByImage($index, img); let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-thumb\"\n [class.sd-preview-thumb--active]=\"i === _activeIndex\"\n [id]=\"'sd-preview-thumb-' + i\"\n [attr.aria-label]=\"img.name\"\n [attr.aria-current]=\"i === _activeIndex ? 'true' : null\"\n [attr.data-autoId]=\"autoIdThumb(i)\"\n (click)=\"onClickThumbnailImage(i)\">\n @if (img.error) {\n <div class=\"sd-preview-thumb__broken\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n } @else {\n <img class=\"sd-preview-thumb__img\" [src]=\"img.blobUrl || img.url\" [alt]=\"img.alt || img.name\" />\n }\n <span class=\"sd-preview-thumb__badge\">{{ i + 1 }}</span>\n @if (_thumbnailPosition === 'right' || _thumbnailPosition === 'left') {\n <span class=\"sd-preview-thumb__name\" [title]=\"img.name\">{{ img.name }}</span>\n }\n </button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [":host{--sd-preview-shell-bg: #0d0e10;--sd-preview-shell-bg-2: #17191c;--sd-preview-panel-bg: rgba(15, 17, 20, .92);--sd-preview-divider: rgba(255, 255, 255, .08);--sd-preview-fg: #f5f6f7;--sd-preview-fg-muted: rgba(255, 255, 255, .6);--sd-preview-fg-subtle: rgba(255, 255, 255, .4);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .18));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .1);--sd-preview-danger-border: rgba(186, 26, 26, .3);--sd-preview-hover-bg: rgba(255, 255, 255, .1);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-preview-shell-bg: #f7f8fa;--sd-preview-shell-bg-2: #ffffff;--sd-preview-panel-bg: rgba(255, 255, 255, .94);--sd-preview-divider: rgba(0, 0, 0, .08);--sd-preview-fg: #1a1c1f;--sd-preview-fg-muted: rgba(0, 0, 0, .62);--sd-preview-fg-subtle: rgba(0, 0, 0, .42);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .16));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .08);--sd-preview-danger-border: rgba(186, 26, 26, .25);--sd-preview-hover-bg: rgba(0, 0, 0, .06)}.sd-preview-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-preview-shell-bg);color:var(--sd-preview-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-shell--fullscreen{height:100vh;width:100vw;border-radius:0}:host(.sd-preview-image-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-header{position:relative;display:flex;align-items:center;height:68px;padding:0 16px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-preview-divider);flex:0 0 auto;z-index:2}.sd-preview-header--simple{justify-content:space-between}.sd-preview-header__left{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto}.sd-preview-header__icon{color:var(--sd-preview-fg-muted);font-size:20px;width:20px;height:20px}.sd-preview-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-header__name{color:var(--sd-preview-fg);max-width:360px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-header__sub{font-size:12px;color:var(--sd-preview-fg-muted);margin-top:2px}.sd-preview-header__counter{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:13px;font-weight:500;color:var(--sd-preview-fg-muted);background:#ffffff14;padding:4px 12px;border-radius:999px;font-variant-numeric:tabular-nums}.sd-preview-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}.sd-preview-shell--pos-bottom .sd-preview-body{flex-direction:column}.sd-preview-shell--pos-top .sd-preview-body{flex-direction:column-reverse}.sd-preview-shell--pos-right .sd-preview-body{flex-direction:row}.sd-preview-shell--pos-left .sd-preview-body{flex-direction:row-reverse}.sd-preview-shell--pos-dots .sd-preview-body,.sd-preview-shell--pos-none .sd-preview-body{flex-direction:column}.sd-preview-stage{position:relative;flex:1 1 auto;min-height:0;min-width:0;background:var(--sd-preview-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.02) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.02) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;display:flex;align-items:center;justify-content:center;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.sd-preview-stage--dragging{cursor:grab}:host([data-theme=light]) .sd-preview-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-image{max-width:100%;max-height:100%;object-fit:contain;transform-origin:center center;transition:transform .2s ease;will-change:transform;pointer-events:none}.sd-preview-caption{position:absolute;bottom:96px;left:50%;transform:translate(-50%);max-width:70%;padding:6px 12px;background:#0f1114b3;color:var(--sd-preview-fg);border-radius:8px;font-size:13px;text-align:center}.sd-preview-image-broken,.sd-preview-image-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;width:100%;max-width:480px;padding:24px;text-align:center}.sd-preview-nav{position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;border-radius:999px;border:none;background:#14141699;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--sd-preview-fg);display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 16px #0000004d;transition:background .12s;z-index:3}.sd-preview-nav sd-icon{font-size:28px;width:28px;height:28px}.sd-preview-nav:hover:not(:disabled){background:#ffffff2e}.sd-preview-nav:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-nav:disabled{background:#ffffff0a;opacity:.35;cursor:not-allowed}.sd-preview-nav--prev{left:16px}.sd-preview-nav--next{right:16px}.sd-preview-toolbar{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:4px;padding:6px 8px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-preview-divider);border-radius:12px;box-shadow:0 8px 30px #0006;z-index:3}.sd-preview-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:36px;height:36px;padding:0 8px;background:transparent;border:none;color:var(--sd-preview-fg);border-radius:8px;cursor:pointer;transition:background .12s}.sd-preview-toolbar__btn sd-icon{font-size:20px;width:20px;height:20px}.sd-preview-toolbar__btn--with-label{padding:0 12px}.sd-preview-toolbar__btn:hover:not(:disabled){background:var(--sd-preview-hover-bg)}.sd-preview-toolbar__btn:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-toolbar__btn-label{font-size:13px;font-weight:500}.sd-preview-toolbar__readout{min-width:56px;text-align:center;color:var(--sd-preview-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums}.sd-preview-toolbar__divider{width:1px;height:20px;background:var(--sd-preview-divider);margin:0 4px}.sd-preview-thumbs{display:flex;gap:8px;padding:12px;background:var(--sd-preview-shell-bg-2);overflow:auto;flex:0 0 auto;flex-direction:row;height:116px}.sd-preview-thumbs--vertical{flex-direction:column;width:152px;height:100%}.sd-preview-thumb{position:relative;flex:0 0 auto;width:120px;height:76px;padding:0;border:none;background:transparent;border-radius:8px;outline:1px solid var(--sd-preview-divider);outline-offset:-1px;opacity:.7;overflow:hidden;cursor:pointer;transition:opacity .12s,outline-color .12s}.sd-preview-thumb:hover{opacity:1}.sd-preview-thumb--active{opacity:1;outline:2px solid var(--sd-preview-primary);outline-offset:-2px;box-shadow:inset 0 0 0 2px var(--sd-preview-primary)}.sd-preview-thumb:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-thumb__img{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}.sd-preview-thumb__broken{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--sd-preview-danger-soft);color:var(--sd-preview-danger);pointer-events:none}.sd-preview-thumb__broken sd-icon{font-size:24px;width:24px;height:24px}.sd-preview-thumb__badge{position:absolute;top:4px;right:4px;background:#000000b3;color:#fff;font-size:10px;font-weight:600;padding:1px 5px;border-radius:4px;pointer-events:none}.sd-preview-thumb__name{position:absolute;left:0;right:0;bottom:0;padding:14px 6px 4px;color:var(--sd-preview-fg);font-size:11px;line-height:1.3;background:linear-gradient(to top,rgba(0,0,0,.85),transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none}.sd-preview-thumbs--vertical .sd-preview-thumb{width:128px}.sd-preview-dots{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:6px;z-index:3}.sd-preview-dot{width:8px;height:8px;border:none;background:#ffffff4d;border-radius:999px;padding:0;cursor:pointer;transition:width .18s,background .18s}.sd-preview-dot--active{width:24px;background:#f5f6f7}.sd-preview-dot:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-status{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:24px}.sd-preview-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-preview-divider);background:#ffffff0d}.sd-preview-status__tile sd-icon{font-size:48px;width:48px;height:48px;color:var(--sd-preview-fg-subtle)}.sd-preview-status__tile--muted{background:#ffffff0d;border-color:var(--sd-preview-divider)}.sd-preview-status__tile--danger{background:var(--sd-preview-danger-soft);border-color:var(--sd-preview-danger-border)}.sd-preview-status__tile--danger sd-icon{color:var(--sd-preview-danger)}.sd-preview-status__title{font-size:16px;font-weight:500;color:var(--sd-preview-fg)}.sd-preview-status__hint{font-size:13px;color:var(--sd-preview-fg-muted);max-width:320px}.sd-preview-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-preview-primary);animation:sd-preview-spin .9s linear infinite}@keyframes sd-preview-spin{to{transform:rotate(360deg)}}.sd-preview-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-preview-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-btn-primary:hover{filter:brightness(1.1)}.sd-preview-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-kbd-hint{position:absolute;top:16px;left:16px;display:flex;align-items:center;gap:6px;z-index:2;pointer-events:none}.sd-preview-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 5px;border:1px solid var(--sd-preview-divider);background:#ffffff0a;color:var(--sd-preview-fg-muted);border-radius:4px;font:500 11px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-kbd-text,.sd-preview-kbd-dot{font-size:11px;color:var(--sd-preview-fg-subtle)}.sd-preview-kbd-dot{margin:0 2px}\n"] }]
664
+ }, template: "<!--\n Self-contained viewer shell. Component sizes to its parent (width:100%;\n height:100%) \u2014 consumer decides whether to wrap in <sd-modal>,\n <sd-side-drawer>, a page section, or use inline.\n-->\n<div [ngClass]=\"shellClass()\" role=\"region\">\n @let _stage = stage();\n @let _images = images();\n @let _thumbnailPosition = thumbnailPosition();\n @let _activeIndex = activeIndex();\n @let _activeImage = activeImage();\n\n <!-- ============================================\n HEADER BAR\n ============================================ -->\n @if (_stage !== 'empty' && _activeImage; as img) {\n <div class=\"sd-preview-header\">\n <div class=\"sd-preview-header__left\">\n <sd-icon class=\"sd-preview-header__icon\" fontIcon=\"image\"></sd-icon>\n <div class=\"sd-preview-header__meta\">\n <div class=\"sd-preview-header__name T14M\" [title]=\"img.name\">{{ img.name }}</div>\n @if (img.size) {\n <div class=\"sd-preview-header__sub\">\n {{ formatBytes(img.size) }}\n </div>\n }\n </div>\n </div>\n\n @if (_images.length > 1) {\n <div class=\"sd-preview-header__counter\" aria-live=\"polite\">\n {{ _activeIndex + 1 }} / {{ _images.length }}\n </div>\n }\n </div>\n } @else if (title()) {\n <!-- Optional override title even on empty state -->\n <div class=\"sd-preview-header sd-preview-header--simple\">\n <div class=\"sd-preview-header__name T14M\">{{ title() }}</div>\n </div>\n }\n\n <!-- ============================================\n BODY (stage + thumbnails per layout)\n ============================================ -->\n <div class=\"sd-preview-body\">\n\n <!-- STAGE ............................................................... -->\n <div\n class=\"sd-preview-stage\"\n [id]=\"stageId\"\n role=\"tabpanel\"\n [class.sd-preview-stage--dragging]=\"zoom() > 1\"\n (wheel)=\"onWheel($event)\"\n (pointerdown)=\"onPointerDown($event)\"\n (pointermove)=\"onPointerMove($event)\"\n (pointerup)=\"onPointerUp($event)\"\n (pointercancel)=\"onPointerCancel($event)\"\n (dblclick)=\"onImageDblClick()\">\n\n @switch (_stage) {\n <!-- why: c\u1EA3 hai tr\u1EA1ng th\u00E1i n\u00E0y \u0111\u1EC1u xu\u1EA5t hi\u1EC7n B\u1EA4T \u0110\u1ED2NG B\u1ED8 (sau khi normalize/t\u1EA3i \u1EA3nh) n\u00EAn\n c\u1EA7n live region, n\u1EBFu kh\u00F4ng screen reader kh\u00F4ng bi\u1EBFt viewer \u0111\u00E3 r\u1ED7ng hay \u0111ang t\u1EA3i. -->\n @case ('empty') {\n <div class=\"sd-preview-status\" role=\"status\" aria-live=\"polite\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--muted\">\n <sd-icon fontIcon=\"image_not_supported\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.no-image' | sdTranslate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.no-image-hint' | sdTranslate }}\n </div>\n </div>\n }\n @case ('loading') {\n <div class=\"sd-preview-status\" role=\"status\" aria-live=\"polite\">\n <div class=\"sd-preview-spinner\" role=\"progressbar\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | sdTranslate }}\n </div>\n </div>\n }\n @default {\n @if (_activeImage; as img) {\n @if (img.loading) {\n <!-- Per-image loading (retry inline) \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-loading\" role=\"status\"\n [attr.aria-label]=\"'core.component.preview-image.loading' | sdTranslate\">\n <div class=\"sd-preview-spinner\"></div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.loading' | sdTranslate }}\n </div>\n </div>\n } @else if (img.error) {\n <!-- Inline broken-image placeholder \u2014 gi\u1EEF nav/dots/thumbs visible -->\n <div class=\"sd-preview-image-broken\" role=\"img\"\n [attr.aria-label]=\"'core.component.preview-image.load-error' | sdTranslate\">\n <div class=\"sd-preview-status__tile sd-preview-status__tile--danger\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n <div class=\"sd-preview-status__title\">\n {{ 'core.component.preview-image.load-error' | sdTranslate }}\n </div>\n <div class=\"sd-preview-status__hint\">\n {{ 'core.component.preview-image.load-error-hint' | sdTranslate }}\n </div>\n <button type=\"button\" class=\"sd-preview-btn-primary\" (click)=\"retryActive()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-image.retry' | sdTranslate }}</span>\n </button>\n </div>\n } @else {\n <img\n class=\"sd-preview-image\"\n [src]=\"img.blobUrl || img.url\"\n [alt]=\"img.alt || img.name\"\n [style.transform]=\"imageTransform()\"\n (error)=\"onImageError()\"\n draggable=\"false\" />\n\n @if (img.caption) {\n <div class=\"sd-preview-caption\">{{ img.caption }}</div>\n }\n }\n }\n }\n }\n\n <!-- KEYBOARD HINT ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-kbd-hint\" aria-hidden=\"true\">\n <span class=\"sd-preview-kbd\">\u2190</span>\n <span class=\"sd-preview-kbd\">\u2192</span>\n <span class=\"sd-preview-kbd-text\">{{ 'core.component.preview-image.kbd-hint-nav' | sdTranslate }}</span>\n </div>\n }\n\n <!-- NAV ARROWS ............................................. -->\n @if (_stage === 'ready' && _images.length > 1) {\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--prev\"\n [disabled]=\"!hasPrev()\"\n [attr.aria-label]=\"'core.component.preview-image.prev' | sdTranslate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"updateCurrentImage(-1)\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-nav sd-preview-nav--next\"\n [disabled]=\"!hasNext()\"\n [attr.aria-label]=\"'core.component.preview-image.next' | sdTranslate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"updateCurrentImage(1)\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n }\n\n <!-- DOTS INDICATOR ......................................... -->\n <!-- why: role=\"tablist\" \u1EDF \u0111\u00E2y l\u00E0 ARIA SAI c\u1EA5u tr\u00FAc \u2014 con c\u1EE7a n\u00F3 l\u00E0 <button> tr\u1EA7n, kh\u00F4ng c\u00F3\n role=\"tab\", kh\u00F4ng aria-selected, v\u00E0 c\u0169ng kh\u00F4ng c\u00F3 tabpanel n\u00E0o \u0111\u1EC3 tr\u1ECF t\u1EDBi. M\u1ED9t tablist\n r\u1ED7ng tab khi\u1EBFn screen reader th\u00F4ng b\u00E1o \"danh s\u00E1ch tab, 0 tab\". C\u00E1c ch\u1EA5m n\u00E0y th\u1EF1c ch\u1EA5t l\u00E0\n b\u1ED9 ch\u1ECDn \u1EA3nh, n\u00EAn ho\u00E0n thi\u1EC7n pattern: tablist + tab + aria-selected + aria-controls tr\u1ECF\n v\u1EC1 v\u00F9ng stage. Nh\u00E3n c\u0169ng s\u1EEDa: tr\u01B0\u1EDBc \u0111\u00E2y M\u1ECCI ch\u1EA5m \u0111\u1EC1u \u0111\u1ECDc l\u00E0 \"\u1EA2nh ti\u1EBFp theo\". -->\n @if (_thumbnailPosition === 'dots' && _stage === 'ready' && _images.length > 1) {\n <div class=\"sd-preview-dots\" role=\"tablist\" [attr.aria-label]=\"'core.component.preview-image.title' | sdTranslate\">\n @for (img of _images; track img.id; let i = $index) {\n <button\n type=\"button\"\n role=\"tab\"\n class=\"sd-preview-dot\"\n [class.sd-preview-dot--active]=\"i === _activeIndex\"\n [attr.aria-selected]=\"i === _activeIndex\"\n [attr.aria-controls]=\"stageId\"\n [attr.tabindex]=\"i === _activeIndex ? 0 : -1\"\n [attr.aria-label]=\"img.alt || img.name\"\n [attr.data-autoId]=\"autoIdDot(i)\"\n (click)=\"onClickThumbnailImage(i)\"></button>\n }\n </div>\n }\n\n <!-- FLOATING TOOLBAR ....................................... -->\n @if (showToolbar() && _stage === 'ready') {\n <div class=\"sd-preview-toolbar\" role=\"toolbar\">\n @if (zoomable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-out' | sdTranslate\"\n [attr.title]=\"'core.component.preview-image.zoom-out' | sdTranslate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"zoom_out\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__readout\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.zoom-in' | sdTranslate\"\n [attr.title]=\"'core.component.preview-image.zoom-in' | sdTranslate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"zoom_in\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn sd-preview-toolbar__btn--with-label\"\n [attr.aria-label]=\"'core.component.preview-image.fit' | sdTranslate\"\n [attr.data-autoId]=\"autoIdFit()\"\n (click)=\"fitToScreen()\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span class=\"sd-preview-toolbar__btn-label\">{{ 'core.component.preview-image.fit' | sdTranslate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.rotate' | sdTranslate\"\n [attr.title]=\"'core.component.preview-image.rotate' | sdTranslate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n <span class=\"sd-preview-toolbar__divider\"></span>\n @if (downloadable()) {\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.download' | sdTranslate\"\n [attr.title]=\"'core.component.preview-image.download' | sdTranslate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadCurrent()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n <button\n type=\"button\"\n class=\"sd-preview-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-image.fullscreen' | sdTranslate\"\n [attr.title]=\"'core.component.preview-image.fullscreen' | sdTranslate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n </div>\n }\n </div>\n\n <!-- THUMBNAIL STRIP .................................................... -->\n @if (\n _stage === 'ready' &&\n _images.length > 1 &&\n _thumbnailPosition !== 'none' &&\n _thumbnailPosition !== 'dots'\n ) {\n <div\n class=\"sd-preview-thumbs\"\n [class.sd-preview-thumbs--vertical]=\"_thumbnailPosition === 'left' || _thumbnailPosition === 'right'\">\n @for (img of _images; track trackByImage($index, img); let i = $index) {\n <button\n type=\"button\"\n class=\"sd-preview-thumb\"\n [class.sd-preview-thumb--active]=\"i === _activeIndex\"\n [id]=\"'sd-preview-thumb-' + i\"\n [attr.aria-label]=\"img.name\"\n [attr.aria-current]=\"i === _activeIndex ? 'true' : null\"\n [attr.data-autoId]=\"autoIdThumb(i)\"\n (click)=\"onClickThumbnailImage(i)\">\n @if (img.error) {\n <div class=\"sd-preview-thumb__broken\">\n <sd-icon fontIcon=\"broken_image\"></sd-icon>\n </div>\n } @else {\n <img class=\"sd-preview-thumb__img\" [src]=\"img.blobUrl || img.url\" [alt]=\"img.alt || img.name\" />\n }\n <span class=\"sd-preview-thumb__badge\">{{ i + 1 }}</span>\n @if (_thumbnailPosition === 'right' || _thumbnailPosition === 'left') {\n <span class=\"sd-preview-thumb__name\" [title]=\"img.name\">{{ img.name }}</span>\n }\n </button>\n }\n </div>\n }\n </div>\n</div>\n", styles: [":host{--sd-preview-shell-bg: #0d0e10;--sd-preview-shell-bg-2: #17191c;--sd-preview-panel-bg: rgba(15, 17, 20, .92);--sd-preview-divider: rgba(255, 255, 255, .08);--sd-preview-fg: #f5f6f7;--sd-preview-fg-muted: rgba(255, 255, 255, .6);--sd-preview-fg-subtle: rgba(255, 255, 255, .4);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .18));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .1);--sd-preview-danger-border: rgba(186, 26, 26, .3);--sd-preview-hover-bg: rgba(255, 255, 255, .1);display:block;width:100%;height:100%;min-height:320px;outline:none}:host([data-theme=light]){--sd-preview-shell-bg: #f7f8fa;--sd-preview-shell-bg-2: #ffffff;--sd-preview-panel-bg: rgba(255, 255, 255, .94);--sd-preview-divider: rgba(0, 0, 0, .08);--sd-preview-fg: #1a1c1f;--sd-preview-fg-muted: rgba(0, 0, 0, .62);--sd-preview-fg-subtle: rgba(0, 0, 0, .42);--sd-preview-primary: var(--sd-preview-accent, var(--sd-primary, #005cbb));--sd-preview-primary-soft: var(--sd-preview-accent-soft, rgba(0, 92, 187, .16));--sd-preview-danger: var(--sd-error, #ba1a1a);--sd-preview-success: var(--sd-success, #2e7d32);--sd-preview-danger-soft: rgba(186, 26, 26, .08);--sd-preview-danger-border: rgba(186, 26, 26, .25);--sd-preview-hover-bg: rgba(0, 0, 0, .06)}.sd-preview-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-preview-shell-bg);color:var(--sd-preview-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-shell--fullscreen{height:100vh;width:100vw;border-radius:0}:host(.sd-preview-image-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-header{position:relative;display:flex;align-items:center;height:68px;padding:0 16px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-preview-divider);flex:0 0 auto;z-index:2}.sd-preview-header--simple{justify-content:space-between}.sd-preview-header__left{display:flex;align-items:center;gap:12px;min-width:0;flex:1 1 auto}.sd-preview-header__icon{color:var(--sd-preview-fg-muted);font-size:20px;width:20px;height:20px}.sd-preview-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-header__name{color:var(--sd-preview-fg);max-width:360px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-header__sub{font-size:12px;color:var(--sd-preview-fg-muted);margin-top:2px}.sd-preview-header__counter{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:13px;font-weight:500;color:var(--sd-preview-fg-muted);background:#ffffff14;padding:4px 12px;border-radius:999px;font-variant-numeric:tabular-nums}.sd-preview-body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;overflow:hidden}.sd-preview-shell--pos-bottom .sd-preview-body{flex-direction:column}.sd-preview-shell--pos-top .sd-preview-body{flex-direction:column-reverse}.sd-preview-shell--pos-right .sd-preview-body{flex-direction:row}.sd-preview-shell--pos-left .sd-preview-body{flex-direction:row-reverse}.sd-preview-shell--pos-dots .sd-preview-body,.sd-preview-shell--pos-none .sd-preview-body{flex-direction:column}.sd-preview-stage{position:relative;flex:1 1 auto;min-height:0;min-width:0;background:var(--sd-preview-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.02) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.02) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.02) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;display:flex;align-items:center;justify-content:center;overflow:hidden;touch-action:none;-webkit-user-select:none;user-select:none}.sd-preview-stage--dragging{cursor:grab}:host([data-theme=light]) .sd-preview-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-image{max-width:100%;max-height:100%;object-fit:contain;transform-origin:center center;transition:transform .2s ease;will-change:transform;pointer-events:none}.sd-preview-caption{position:absolute;bottom:96px;left:50%;transform:translate(-50%);max-width:70%;padding:6px 12px;background:#0f1114b3;color:var(--sd-preview-fg);border-radius:8px;font-size:13px;text-align:center}.sd-preview-image-broken,.sd-preview-image-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;width:100%;max-width:480px;padding:24px;text-align:center}.sd-preview-nav{position:absolute;top:50%;transform:translateY(-50%);width:48px;height:48px;border-radius:999px;border:none;background:#14141699;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);color:var(--sd-preview-fg);display:flex;align-items:center;justify-content:center;cursor:pointer;box-shadow:0 4px 16px #0000004d;transition:background .12s;z-index:3}.sd-preview-nav sd-icon{font-size:28px;width:28px;height:28px}.sd-preview-nav:hover:not(:disabled){background:#ffffff2e}.sd-preview-nav:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-nav:disabled{background:#ffffff0a;opacity:.35;cursor:not-allowed}.sd-preview-nav--prev{left:16px}.sd-preview-nav--next{right:16px}.sd-preview-toolbar{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:4px;padding:6px 8px;background:var(--sd-preview-panel-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-preview-divider);border-radius:12px;box-shadow:0 8px 30px #0006;z-index:3}.sd-preview-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:36px;height:36px;padding:0 8px;background:transparent;border:none;color:var(--sd-preview-fg);border-radius:8px;cursor:pointer;transition:background .12s}.sd-preview-toolbar__btn sd-icon{font-size:20px;width:20px;height:20px}.sd-preview-toolbar__btn--with-label{padding:0 12px}.sd-preview-toolbar__btn:hover:not(:disabled){background:var(--sd-preview-hover-bg)}.sd-preview-toolbar__btn:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-toolbar__btn-label{font-size:13px;font-weight:500}.sd-preview-toolbar__readout{min-width:56px;text-align:center;color:var(--sd-preview-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums}.sd-preview-toolbar__divider{width:1px;height:20px;background:var(--sd-preview-divider);margin:0 4px}.sd-preview-thumbs{display:flex;gap:8px;padding:12px;background:var(--sd-preview-shell-bg-2);overflow:auto;flex:0 0 auto;flex-direction:row;height:116px}.sd-preview-thumbs--vertical{flex-direction:column;width:152px;height:100%}.sd-preview-thumb{position:relative;flex:0 0 auto;width:120px;height:76px;padding:0;border:none;background:transparent;border-radius:8px;outline:1px solid var(--sd-preview-divider);outline-offset:-1px;opacity:.7;overflow:hidden;cursor:pointer;transition:opacity .12s,outline-color .12s}.sd-preview-thumb:hover{opacity:1}.sd-preview-thumb--active{opacity:1;outline:2px solid var(--sd-preview-primary);outline-offset:-2px;box-shadow:inset 0 0 0 2px var(--sd-preview-primary)}.sd-preview-thumb:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-thumb__img{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}.sd-preview-thumb__broken{display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--sd-preview-danger-soft);color:var(--sd-preview-danger);pointer-events:none}.sd-preview-thumb__broken sd-icon{font-size:24px;width:24px;height:24px}.sd-preview-thumb__badge{position:absolute;top:4px;right:4px;background:#000000b3;color:#fff;font-size:10px;font-weight:600;padding:1px 5px;border-radius:4px;pointer-events:none}.sd-preview-thumb__name{position:absolute;left:0;right:0;bottom:0;padding:14px 6px 4px;color:var(--sd-preview-fg);font-size:11px;line-height:1.3;background:linear-gradient(to top,rgba(0,0,0,.85),transparent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;pointer-events:none}.sd-preview-thumbs--vertical .sd-preview-thumb{width:128px}.sd-preview-dots{position:absolute;bottom:24px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:6px;z-index:3}.sd-preview-dot{width:8px;height:8px;border:none;background:#ffffff4d;border-radius:999px;padding:0;cursor:pointer;transition:width .18s,background .18s}.sd-preview-dot--active{width:24px;background:#f5f6f7}.sd-preview-dot:focus-visible{outline:2px solid var(--sd-preview-primary);outline-offset:2px}.sd-preview-status{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:24px}.sd-preview-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-preview-divider);background:#ffffff0d}.sd-preview-status__tile sd-icon{font-size:48px;width:48px;height:48px;color:var(--sd-preview-fg-subtle)}.sd-preview-status__tile--muted{background:#ffffff0d;border-color:var(--sd-preview-divider)}.sd-preview-status__tile--danger{background:var(--sd-preview-danger-soft);border-color:var(--sd-preview-danger-border)}.sd-preview-status__tile--danger sd-icon{color:var(--sd-preview-danger)}.sd-preview-status__title{font-size:16px;font-weight:500;color:var(--sd-preview-fg)}.sd-preview-status__hint{font-size:13px;color:var(--sd-preview-fg-muted);max-width:320px}.sd-preview-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-preview-primary);animation:sd-preview-spin .9s linear infinite}@keyframes sd-preview-spin{to{transform:rotate(360deg)}}.sd-preview-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-preview-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-btn-primary:hover{filter:brightness(1.1)}.sd-preview-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-kbd-hint{position:absolute;top:16px;left:16px;display:flex;align-items:center;gap:6px;z-index:2;pointer-events:none}.sd-preview-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:20px;padding:0 5px;border:1px solid var(--sd-preview-divider);background:#ffffff0a;color:var(--sd-preview-fg-muted);border-radius:4px;font:500 11px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-kbd-text,.sd-preview-kbd-dot{font-size:11px;color:var(--sd-preview-fg-subtle)}.sd-preview-kbd-dot{margin:0 2px}\n"] }]
655
665
  }], ctorParameters: () => [], propDecorators: { onKeyDown: [{
656
666
  type: HostListener,
657
667
  args: ['keydown', ['$event']]
@@ -3212,12 +3222,12 @@ class SdPreviewPdf {
3212
3222
  img.src = dataUrl;
3213
3223
  }
3214
3224
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdPreviewPdf, deps: [], target: i0.ɵɵFactoryTarget.Component });
3215
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdPreviewPdf, isStandalone: true, selector: "sd-preview-pdf", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, startPage: { classPropertyName: "startPage", publicName: "startPage", isSignal: true, isRequired: false, transformFunction: null }, initialZoom: { classPropertyName: "initialZoom", publicName: "initialZoom", isSignal: true, isRequired: false, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: false, transformFunction: null }, sidebarOpen: { classPropertyName: "sidebarOpen", publicName: "sidebarOpen", isSignal: true, isRequired: false, transformFunction: null }, scrollMode: { classPropertyName: "scrollMode", publicName: "scrollMode", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, downloadable: { classPropertyName: "downloadable", publicName: "downloadable", isSignal: true, isRequired: false, transformFunction: null }, password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null }, httpHeaders: { classPropertyName: "httpHeaders", publicName: "httpHeaders", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", loaded: "loaded", pageChange: "pageChange", zoomChange: "zoomChange", download: "download", loadError: "loadError", searchChange: "searchChange" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.data-theme": "theme()", "class.sd-preview-pdf-host--fullscreen": "isFullscreen()" }, classAttribute: "sd-preview-pdf-host" }, viewQueries: [{ propertyName: "pageCanvasRef", first: true, predicate: ["pageCanvas"], descendants: true, isSignal: true }, { propertyName: "stageEl", first: true, predicate: ["stageRef"], descendants: true, isSignal: true }, { propertyName: "pageInputRef", first: true, predicate: ["pageInput"], descendants: true, isSignal: true }, { propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "sidebarContentRef", first: true, predicate: ["sidebarContent"], descendants: true, isSignal: true }, { propertyName: "outlineItemRefs", predicate: ["outlineItem"], descendants: true, isSignal: true }, { propertyName: "sidebarTabRefs", predicate: ["sidebarTab"], descendants: true, isSignal: true }, { propertyName: "continuousCanvases", predicate: ["continuousCanvas"], descendants: true, isSignal: true }, { propertyName: "thumbCanvases", predicate: ["thumbCanvas"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n sd-preview-pdf shell. Self-contained \u2014 sizes to its parent. Consumer wraps\n in <sd-modal>/<sd-side-drawer>/page section. Header + sidebar + stage +\n floating toolbar mirror Artboards A / C / E from the design handoff.\n-->\n<div class=\"sd-preview-pdf-shell\" role=\"region\">\n @let _stage = stage();\n @let _numPages = numPages();\n @let _activePage = activePage();\n @let _isSidebarOpen = isSidebarOpen();\n @let _searchBarOpen = searchBarOpen();\n @let _searchTerm = searchTerm();\n @let _searchTotal = searchTotal();\n\n <!-- ============================================\n HEADER BAR \u2014 hidden in empty + loading per design\n ============================================ -->\n @if (_stage === 'ready') {\n <div class=\"sd-preview-pdf-header\">\n <div class=\"sd-preview-pdf-header__left\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_isSidebarOpen\"\n [attr.aria-expanded]=\"_isSidebarOpen\"\n [attr.aria-controls]=\"sidebarRegionId()\"\n [attr.aria-label]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.title]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.data-autoId]=\"autoIdSidebarToggle()\"\n (click)=\"toggleSidebar()\">\n <sd-icon fontIcon=\"menu\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-header__divider\"></span>\n <div class=\"sd-preview-pdf-fileicon\" aria-hidden=\"true\">PDF</div>\n <div class=\"sd-preview-pdf-header__meta\">\n <div class=\"sd-preview-pdf-header__name\" [title]=\"title() || filename()\">\n {{ title() || filename() }}\n </div>\n <div class=\"sd-preview-pdf-header__sub\">\n {{ 'core.component.preview-pdf.pages-count' | translate: { count: _numPages } }}\n </div>\n </div>\n </div>\n\n <div class=\"sd-preview-pdf-header__right\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_searchBarOpen\"\n [attr.aria-expanded]=\"_searchBarOpen\"\n [attr.aria-controls]=\"searchBarId()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search' | translate\"\n [attr.data-autoId]=\"autoIdSearchToggle()\"\n (click)=\"searchBarOpen() ? closeSearch() : openSearch()\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n </button>\n @if (canPrint()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.print' | translate\"\n [attr.title]=\"'core.component.preview-pdf.print' | translate\"\n [attr.data-autoId]=\"autoIdPrint()\"\n (click)=\"printFile()\">\n <sd-icon fontIcon=\"print\"></sd-icon>\n </button>\n }\n @if (canDownload()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.download' | translate\"\n [attr.title]=\"'core.component.preview-pdf.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n @if (canFullscreen()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n }\n </div>\n </div>\n\n <!-- ============================================\n SEARCH BAR \u2014 between header + body, only when bar is open\n ============================================ -->\n <div\n class=\"sd-preview-pdf-searchbar\"\n role=\"search\"\n [id]=\"searchBarId()\"\n [hidden]=\"!_searchBarOpen\"\n [style.display]=\"_searchBarOpen ? null : 'none'\">\n @if (_searchBarOpen) {\n <sd-icon class=\"sd-preview-pdf-searchbar__icon\" fontIcon=\"search\"></sd-icon>\n <input\n #searchInput\n type=\"text\"\n class=\"sd-preview-pdf-searchbar__input\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-placeholder' | translate\"\n [attr.placeholder]=\"'core.component.preview-pdf.search-placeholder' | translate\"\n [value]=\"_searchTerm\"\n [attr.data-autoId]=\"autoIdSearchInput()\"\n (input)=\"onSearchInput($event)\" />\n <span class=\"sd-preview-pdf-searchbar__counter\" aria-live=\"polite\">\n {{ searchCurrent() }} / {{ _searchTotal }}{{ searchTruncated() ? '+' : '' }}\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.data-autoId]=\"autoIdSearchPrev()\"\n (click)=\"searchPrev()\">\n <sd-icon fontIcon=\"keyboard_arrow_up\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.data-autoId]=\"autoIdSearchNext()\"\n (click)=\"searchNext()\">\n <sd-icon fontIcon=\"keyboard_arrow_down\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__divider\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchCaseSensitive()\"\n [attr.aria-pressed]=\"searchCaseSensitive()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.data-autoId]=\"autoIdSearchCase()\"\n (click)=\"toggleSearchCaseSensitive()\">\n <sd-icon fontIcon=\"match_case\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchWholeWord()\"\n [attr.aria-pressed]=\"searchWholeWord()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.data-autoId]=\"autoIdSearchWhole()\"\n (click)=\"toggleSearchWholeWord()\">\n <sd-icon fontIcon=\"text_fields\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__spacer\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.data-autoId]=\"autoIdSearchClose()\"\n (click)=\"closeSearch()\">\n <sd-icon fontIcon=\"close\"></sd-icon>\n </button>\n }\n </div>\n }\n\n <!-- ============================================\n BODY \u2014 sidebar + stage\n ============================================ -->\n <div class=\"sd-preview-pdf-body\">\n <!-- SIDEBAR ............................................................. -->\n @if (_stage === 'ready') {\n <aside\n class=\"sd-preview-pdf-sidebar\"\n role=\"complementary\"\n [id]=\"sidebarRegionId()\"\n [hidden]=\"!_isSidebarOpen\"\n [style.display]=\"_isSidebarOpen ? null : 'none'\">\n @if (_isSidebarOpen) {\n @let _sidebarMode = sidebarMode();\n <div class=\"sd-preview-pdf-sidebar__tabs\" role=\"tablist\">\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabThumbnailsId()\"\n [attr.aria-selected]=\"_sidebarMode === 'thumbnails'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'thumbnails' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'thumbnails'\"\n data-sidebar-mode=\"thumbnails\"\n [attr.data-autoId]=\"autoIdTabThumbnails()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'thumbnails')\"\n (click)=\"setSidebarMode('thumbnails')\">\n <sd-icon fontIcon=\"view_carousel\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.thumbnails' | translate }}</span>\n </button>\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabOutlineId()\"\n [attr.aria-selected]=\"_sidebarMode === 'outline'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'outline' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'outline'\"\n data-sidebar-mode=\"outline\"\n [attr.data-autoId]=\"autoIdTabOutline()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'outline')\"\n (click)=\"setSidebarMode('outline')\">\n <sd-icon fontIcon=\"list\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.outline' | translate }}</span>\n </button>\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabSearchId()\"\n [attr.aria-selected]=\"_sidebarMode === 'search'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'search' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'search'\"\n data-sidebar-mode=\"search\"\n [attr.data-autoId]=\"autoIdTabSearch()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'search')\"\n (click)=\"setSidebarMode('search')\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.search' | translate }}</span>\n </button>\n </div>\n\n <div\n #sidebarContent\n class=\"sd-preview-pdf-sidebar__content\"\n role=\"tabpanel\"\n [id]=\"sidebarPanelId()\"\n [attr.aria-labelledby]=\"sidebarActiveTabId()\"\n (scroll)=\"onThumbnailSidebarScroll($event)\">\n @switch (_sidebarMode) {\n @case ('thumbnails') {\n <ul class=\"sd-preview-pdf-thumb-list\">\n <li class=\"sd-preview-pdf-thumb-list__spacer\" aria-hidden=\"true\" [style.height.px]=\"thumbnailTopSpacer()\"></li>\n @for (p of pageNumbers(); track trackByPage($index, p)) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-thumb\"\n [class.sd-preview-pdf-thumb--active]=\"p === _activePage\"\n [attr.aria-current]=\"p === _activePage ? 'page' : null\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: p, total: _numPages }\"\n [attr.data-autoId]=\"autoIdThumb(p - 1)\"\n (click)=\"goToPage(p)\">\n <div class=\"sd-preview-pdf-thumb__page\">\n <canvas #thumbCanvas class=\"sd-preview-pdf-thumb__canvas\" [attr.data-page]=\"p\" aria-hidden=\"true\"></canvas>\n </div>\n <span class=\"sd-preview-pdf-thumb__label\">{{ p }}</span>\n </button>\n </li>\n }\n <li class=\"sd-preview-pdf-thumb-list__spacer\" aria-hidden=\"true\" [style.height.px]=\"thumbnailBottomSpacer()\"></li>\n </ul>\n }\n @case ('outline') {\n <ul\n class=\"sd-preview-pdf-outline-tree\"\n role=\"tree\"\n [attr.aria-label]=\"'core.component.preview-pdf.tab.outline' | translate\">\n @for (row of visibleOutline(); track row.item.id) {\n <li class=\"sd-preview-pdf-outline-tree__node\" role=\"none\">\n <div class=\"sd-preview-pdf-outline-tree__row\" [style.padding-left.px]=\"(row.level - 1) * 16\">\n @if (row.item.children.length > 0) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-outline-tree__toggle\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.item.title\"\n [attr.aria-expanded]=\"isOutlineExpanded(row.item.id)\"\n (click)=\"toggleOutlineItem(row.item.id, $event)\">\n <sd-icon [fontIcon]=\"isOutlineExpanded(row.item.id) ? 'expand_more' : 'chevron_right'\"></sd-icon>\n </button>\n } @else {\n <span class=\"sd-preview-pdf-outline-tree__toggle-placeholder\" aria-hidden=\"true\"></span>\n }\n\n @if (row.item.url && row.item.page === null) {\n <a\n #outlineItem\n class=\"sd-preview-pdf-outline-tree__item\"\n role=\"treeitem\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n [href]=\"row.item.url\"\n [attr.data-outline-id]=\"row.item.id\"\n [attr.aria-level]=\"row.level\"\n [attr.aria-expanded]=\"row.item.children.length > 0 ? isOutlineExpanded(row.item.id) : null\"\n [attr.aria-current]=\"row.item.page === _activePage ? 'page' : null\"\n [attr.tabindex]=\"outlineFocusId() === row.item.id ? 0 : -1\"\n (focus)=\"focusOutlineItem(row.item.id)\"\n (keydown)=\"onOutlineKeyDown($event, row.item.id)\">\n {{ row.item.title }}\n </a>\n } @else {\n <button\n #outlineItem\n type=\"button\"\n class=\"sd-preview-pdf-outline-tree__item\"\n role=\"treeitem\"\n [attr.data-outline-id]=\"row.item.id\"\n [attr.aria-level]=\"row.level\"\n [attr.aria-expanded]=\"row.item.children.length > 0 ? isOutlineExpanded(row.item.id) : null\"\n [attr.aria-current]=\"row.item.page === _activePage ? 'page' : null\"\n [attr.tabindex]=\"outlineFocusId() === row.item.id ? 0 : -1\"\n (focus)=\"focusOutlineItem(row.item.id)\"\n (click)=\"activateOutlineItem(row.item.id)\"\n (keydown)=\"onOutlineKeyDown($event, row.item.id)\">\n {{ row.item.title }}\n </button>\n }\n </div>\n </li>\n }\n </ul>\n }\n @case ('search') {\n @if (!_searchTerm) {\n <div class=\"sd-preview-pdf-sidebar__message\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-placeholder' | translate }}</p>\n </div>\n } @else if (_searchTotal === 0) {\n <div class=\"sd-preview-pdf-sidebar__message\">\n <sd-icon fontIcon=\"search_off\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-no-results' | translate }}</p>\n </div>\n } @else {\n @if (searchTruncated()) {\n <p class=\"sd-preview-pdf-search-limit\" role=\"status\">\n {{ 'core.component.preview-pdf.search-truncated' | translate: { count: _searchTotal } }}\n </p>\n }\n <ul class=\"sd-preview-pdf-search-list\">\n @for (r of searchResults(); track $index; let i = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-search-result\"\n [class.sd-preview-pdf-search-result--active]=\"i === searchActiveIndex()\"\n [attr.data-autoId]=\"autoIdResult(i)\"\n (click)=\"activateSearchResult(i)\">\n <span class=\"sd-preview-pdf-search-result__snippet\">\n {{ r.before }}<mark>{{ r.term }}</mark\n >{{ r.after }}\n </span>\n <span class=\"sd-preview-pdf-search-result__page\">{{ r.page }}</span>\n </button>\n </li>\n }\n </ul>\n }\n }\n }\n </div>\n }\n </aside>\n }\n\n <!-- STAGE ............................................................... -->\n <div class=\"sd-preview-pdf-stage\" #stageRef (wheel)=\"onWheel($event)\" (scroll)=\"onStageScroll()\">\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--muted\">\n <sd-icon fontIcon=\"picture_as_pdf\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.empty.title' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.empty.subtitle' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n @let _loadPercent = loadPercent();\n <div class=\"sd-preview-pdf-status\">\n <div\n class=\"sd-preview-pdf-spinner\"\n role=\"progressbar\"\n [attr.aria-label]=\"'core.component.preview-pdf.loading' | translate\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"_loadPercent\"></div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.loading' | translate }}\n @if (_loadPercent > 0) {\n <span class=\"sd-preview-pdf-progress-pct\">&nbsp;{{ _loadPercent }}%</span>\n }\n </div>\n <div class=\"sd-preview-pdf-progress\">\n <div class=\"sd-preview-pdf-progress__fill\" [style.width.%]=\"_loadPercent\"></div>\n </div>\n </div>\n }\n @case ('error') {\n @if (loadError$(); as err) {\n <div class=\"sd-preview-pdf-status\" role=\"alert\" aria-live=\"assertive\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--danger\">\n <sd-icon fontIcon=\"error\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.title' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.body' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__actions\">\n <button type=\"button\" class=\"sd-preview-pdf-btn-primary\" (click)=\"retryLoad()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.retry' | translate }}</span>\n </button>\n @if (canDownload()) {\n <button type=\"button\" class=\"sd-preview-pdf-btn-secondary\" (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.download-raw' | translate }}</span>\n </button>\n }\n </div>\n </div>\n }\n }\n @default {\n @if (scrollModeCurrent() === 'continuous') {\n <div class=\"sd-preview-pdf-continuous-content\">\n <div class=\"sd-preview-pdf-continuous-spacer\" [style.height.px]=\"continuousTopSpacer()\"></div>\n @for (pageNumber of continuousPages(); track pageNumber) {\n <div class=\"sd-preview-pdf-continuous-slot\" [style.min-height.px]=\"continuousPageHeights()[pageNumber]\">\n <canvas\n #continuousCanvas\n class=\"sd-preview-pdf-continuous-page\"\n role=\"img\"\n [attr.data-page]=\"pageNumber\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: pageNumber, total: _numPages }\">\n </canvas>\n </div>\n }\n <div class=\"sd-preview-pdf-continuous-spacer\" [style.height.px]=\"continuousBottomSpacer()\"></div>\n </div>\n } @else {\n <div class=\"sd-preview-pdf-page-wrap\">\n <canvas\n #pageCanvas\n class=\"sd-preview-pdf-page\"\n role=\"img\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages }\">\n </canvas>\n </div>\n }\n }\n }\n\n <!-- FLOATING TOOLBAR ........................................ -->\n @if (showToolbar() && _stage === 'ready') {\n @let _canPrev = canPrev();\n @let _canNext = canNext();\n @let _zoomMode = zoomMode();\n @let _scrollModeCurrent = scrollModeCurrent();\n <div class=\"sd-preview-pdf-toolbar\" role=\"toolbar\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.data-autoId]=\"autoIdFirst()\"\n (click)=\"firstPage()\">\n <sd-icon fontIcon=\"first_page\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"prevPage()\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__page\">\n <input\n type=\"text\"\n inputmode=\"numeric\"\n class=\"sd-preview-pdf-toolbar__page-input\"\n [value]=\"_activePage\"\n #pageInput\n (keydown.enter)=\"onPageInputEnter($event)\"\n (blur)=\"onPageInputBlur($event)\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages }\"\n [attr.data-autoId]=\"autoIdPageInput()\" />\n <span class=\"sd-preview-pdf-toolbar__page-total\">/ {{ _numPages }}</span>\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"nextPage()\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.data-autoId]=\"autoIdLast()\"\n (click)=\"lastPage()\">\n <sd-icon fontIcon=\"last_page\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"remove\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__zoom\" aria-live=\"polite\" [attr.data-autoId]=\"autoIdZoomReadout()\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"add\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-fit'\"\n [attr.data-autoId]=\"autoIdFitPage()\"\n (click)=\"setZoom('page-fit')\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-page' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-width'\"\n [attr.data-autoId]=\"autoIdFitWidth()\"\n (click)=\"setZoom('page-width')\">\n <sd-icon fontIcon=\"swap_horiz\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-width' | translate }}</span>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.title]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [attr.data-autoId]=\"autoIdScrollMode()\"\n (click)=\"setScrollMode(_scrollModeCurrent === 'page' ? 'continuous' : 'page')\">\n <sd-icon [fontIcon]=\"_scrollModeCurrent === 'page' ? 'description' : 'view_day'\"></sd-icon>\n <span>\n {{\n (_scrollModeCurrent === 'page' ? 'core.component.preview-pdf.scroll-page' : 'core.component.preview-pdf.scroll-continuous')\n | translate\n }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{--sd-pdf-shell-bg: #1a1c1f;--sd-pdf-sidebar-bg: #22252a;--sd-pdf-toolbar-bg: rgba(20, 22, 25, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .4);--sd-pdf-divider: rgba(255, 255, 255, .08);--sd-pdf-fg: #f5f6f7;--sd-pdf-fg-muted: rgba(255, 255, 255, .6);--sd-pdf-fg-subtle: rgba(255, 255, 255, .4);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .18));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .1);--sd-pdf-danger-border: rgba(186, 26, 26, .3);--sd-pdf-danger-tint: rgba(186, 26, 26, .15);--sd-pdf-hover-bg: rgba(255, 255, 255, .1);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(249, 115, 22, .85);display:block;width:100%;height:100%;min-height:320px;outline:none}:host:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}:host([data-theme=light]){--sd-pdf-shell-bg: #f7f8fa;--sd-pdf-sidebar-bg: #ffffff;--sd-pdf-toolbar-bg: rgba(255, 255, 255, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .12);--sd-pdf-divider: rgba(0, 0, 0, .08);--sd-pdf-fg: #1a1c1f;--sd-pdf-fg-muted: rgba(0, 0, 0, .62);--sd-pdf-fg-subtle: rgba(0, 0, 0, .42);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .16));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .08);--sd-pdf-danger-border: rgba(186, 26, 26, .25);--sd-pdf-danger-tint: rgba(186, 26, 26, .12);--sd-pdf-hover-bg: rgba(0, 0, 0, .06);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(250, 204, 21, .9)}:host(.sd-preview-pdf-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-pdf-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-pdf-shell-bg);color:var(--sd-pdf-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-pdf-header{display:flex;align-items:center;justify-content:space-between;gap:12px;height:57px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-pdf-divider);flex:0 0 auto;z-index:2}.sd-preview-pdf-header__left{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 auto}.sd-preview-pdf-header__right{display:flex;align-items:center;gap:4px;flex-shrink:0}.sd-preview-pdf-header__divider{display:inline-block;width:1px;height:20px;background:var(--sd-pdf-divider)}.sd-preview-pdf-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-pdf-header__name{font-size:14px;font-weight:500;color:var(--sd-pdf-fg);max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-pdf-header__sub{font-size:11px;color:var(--sd-pdf-fg-muted);margin-top:2px}.sd-preview-pdf-fileicon{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;background:var(--sd-pdf-danger-tint);border:1px solid var(--sd-pdf-danger-border);color:var(--sd-error-light, #f87171);font:700 10px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-pdf-iconbtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:32px;height:32px;padding:0 8px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;transition:background .12s,color .12s;white-space:nowrap;flex-shrink:0}.sd-preview-pdf-iconbtn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-iconbtn__label{font-size:13px;font-weight:500}.sd-preview-pdf-iconbtn:hover:not(:disabled){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-iconbtn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-iconbtn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-iconbtn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-searchbar{display:flex;align-items:center;gap:6px;height:46px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);border-bottom:1px solid var(--sd-pdf-divider);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);flex:0 0 auto;z-index:2}.sd-preview-pdf-searchbar__icon{color:var(--sd-pdf-fg-muted);font-size:18px;width:18px;height:18px;flex-shrink:0}.sd-preview-pdf-searchbar__input{flex:0 1 360px;min-width:160px;height:30px;background:#ffffff0f;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:6px;padding:0 10px;font-size:13px;outline:none}.sd-preview-pdf-searchbar__input::placeholder{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-searchbar__input:focus{border-color:var(--sd-pdf-primary);background:#ffffff17}.sd-preview-pdf-searchbar__counter{color:var(--sd-pdf-fg-muted);font-size:12px;font-variant-numeric:tabular-nums;min-width:48px;text-align:center;flex-shrink:0}.sd-preview-pdf-searchbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);flex-shrink:0}.sd-preview-pdf-searchbar__spacer{flex:1 1 auto}.sd-pdf-search-overlay{position:absolute;top:8px;left:50%;transform:translate(-50%);display:flex;flex-wrap:wrap;gap:4px;max-width:80%;pointer-events:none;z-index:5}.sd-pdf-search-hi{display:inline-block;padding:1px 4px;border-radius:2px;background:var(--sd-pdf-search-hi);color:#000;font-size:12px;font-weight:500}.sd-pdf-search-hi--active{background:var(--sd-pdf-search-hi-active);color:#fff;box-shadow:0 0 0 2px #fff6}.sd-preview-pdf-body{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:row;overflow:hidden}.sd-preview-pdf-sidebar{width:240px;display:flex;flex-direction:column;background:var(--sd-pdf-sidebar-bg);border-right:1px solid var(--sd-pdf-divider);flex-shrink:0;min-height:0}.sd-preview-pdf-sidebar__tabs{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-sidebar__tab{flex:1 1 0;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;padding:0 4px;background:transparent;border:none;color:var(--sd-pdf-fg-muted);border-radius:6px;cursor:pointer;font-size:12px;font-weight:500;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-sidebar__tab sd-icon{font-size:14px;width:14px;height:14px}.sd-preview-pdf-sidebar__tab:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-sidebar__tab--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-sidebar__content{flex:1 1 auto;overflow:auto;padding:12px}.sd-preview-pdf-sidebar__message{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 16px;text-align:center;color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-sidebar__message sd-icon{font-size:28px;width:28px;height:28px;opacity:.6}.sd-preview-pdf-sidebar__message p{margin:0;font-size:12px;line-height:1.5}.sd-preview-pdf-outline-tree{list-style:none;margin:0;padding:0}.sd-preview-pdf-outline-tree__node{margin:0;padding:0}.sd-preview-pdf-outline-tree__row{display:flex;align-items:center;min-width:0}.sd-preview-pdf-outline-tree__toggle,.sd-preview-pdf-outline-tree__toggle-placeholder{width:24px;height:28px;flex:0 0 24px}.sd-preview-pdf-outline-tree__toggle{display:inline-flex;align-items:center;justify-content:center;padding:0;border:0;border-radius:4px;background:transparent;color:var(--sd-pdf-fg-muted);cursor:pointer}.sd-preview-pdf-outline-tree__toggle:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-outline-tree__toggle sd-icon{width:16px;height:16px;font-size:16px}.sd-preview-pdf-outline-tree__item{min-width:0;flex:1 1 auto;overflow:hidden;padding:6px 8px;border:0;border-radius:5px;background:transparent;color:var(--sd-pdf-fg-muted);font:inherit;font-size:12px;line-height:1.35;text-align:left;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.sd-preview-pdf-outline-tree__item:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-outline-tree__item[aria-current=page]{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary);font-weight:600}.sd-preview-pdf-outline-tree__item:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:-2px}.sd-preview-pdf-thumb-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.sd-preview-pdf-thumb-list__spacer{flex:0 0 auto;pointer-events:none}.sd-preview-pdf-search-limit{margin:0 0 8px;color:var(--sd-pdf-fg-muted);font-size:11px;line-height:1.4}.sd-preview-pdf-thumb{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;padding:0;background:transparent;border:none;cursor:pointer}.sd-preview-pdf-thumb__page{width:152px;aspect-ratio:612/792;background:var(--sd-pdf-page-bg);border-radius:2px;outline:1px solid rgba(255,255,255,.08);outline-offset:-1px;box-shadow:0 2px 6px #0000004d;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-user-select:none;user-select:none;background-image:linear-gradient(90deg,#0000,#0000000a,#0000);background-size:200% 100%;animation:sd-preview-pdf-thumb-skel 1.6s ease-in-out infinite}.sd-preview-pdf-thumb__canvas{display:block;max-width:100%;max-height:100%;background:var(--sd-pdf-page-bg)}.sd-preview-pdf-thumb__label{font-size:11px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:-2px}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__label{color:var(--sd-pdf-primary);font-weight:600}.sd-preview-pdf-thumb:focus-visible .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-stage{position:relative;flex:1 1 auto;min-width:0;min-height:0;background:var(--sd-pdf-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.015) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;overflow:auto;padding:32px 24px 96px;display:flex;justify-content:center;align-items:flex-start}:host([data-theme=light]) .sd-preview-pdf-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-pdf-page-wrap{display:inline-block;position:relative}.sd-preview-pdf-page{display:block;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow);max-width:100%}.sd-preview-pdf-continuous-content{width:100%;min-height:100%}.sd-preview-pdf-continuous-spacer{width:1px}.sd-preview-pdf-continuous-slot{display:flex;justify-content:center;align-items:flex-start}.sd-preview-pdf-continuous-slot+.sd-preview-pdf-continuous-slot{margin-top:24px}.sd-preview-pdf-continuous-page{display:block;max-width:100%;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow)}.sd-preview-pdf-toolbar{position:absolute;bottom:20px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:2px;padding:5px 6px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-pdf-divider);border-radius:10px;box-shadow:0 8px 30px #0006;z-index:4;max-width:calc(100% - 32px);overflow-x:auto}.sd-preview-pdf-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:30px;height:28px;padding:0 6px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-toolbar__btn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-toolbar__btn span{font-size:13px;font-weight:500}.sd-preview-pdf-toolbar__btn--with-label{padding:0 10px}.sd-preview-pdf-toolbar__btn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__btn:hover:not(:disabled):not(.sd-preview-pdf-toolbar__btn--active){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-toolbar__btn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-toolbar__btn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-toolbar__page{display:inline-flex;align-items:center;gap:4px;padding:0 6px;flex-shrink:0}.sd-preview-pdf-toolbar__page-input{width:36px;height:24px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:4px;text-align:center;font-size:13px;font-variant-numeric:tabular-nums;outline:none}.sd-preview-pdf-toolbar__page-input:focus{border-color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__page-total{font-size:13px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-toolbar__zoom{min-width:56px;text-align:center;color:var(--sd-pdf-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums;background:#ffffff14;border-radius:4px;padding:2px 8px}.sd-preview-pdf-toolbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);margin:0 4px;flex-shrink:0}.sd-preview-pdf-status{margin:auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:32px;max-width:420px}.sd-preview-pdf-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-status__tile sd-icon{font-size:48px;width:48px;height:48px}.sd-preview-pdf-status__tile--muted{background:#ffffff0d}.sd-preview-pdf-status__tile--muted sd-icon{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-status__tile--danger{background:var(--sd-pdf-danger-soft);border-color:var(--sd-pdf-danger-border)}.sd-preview-pdf-status__tile--danger sd-icon{color:var(--sd-pdf-danger)}.sd-preview-pdf-status__title{font-size:16px;font-weight:500;color:var(--sd-pdf-fg)}.sd-preview-pdf-status__hint{font-size:13px;color:var(--sd-pdf-fg-muted);line-height:1.5}.sd-preview-pdf-status__actions{display:flex;gap:8px;margin-top:4px}.sd-preview-pdf-progress-pct{font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted)}.sd-preview-pdf-progress{width:240px;height:4px;background:#ffffff14;border-radius:999px;overflow:hidden}.sd-preview-pdf-progress__fill{height:100%;background:var(--sd-pdf-primary);transition:width .12s linear}.sd-preview-pdf-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-pdf-primary);animation:sd-preview-pdf-spin .9s linear infinite}@keyframes sd-preview-pdf-spin{to{transform:rotate(360deg)}}@keyframes sd-preview-pdf-thumb-skel{0%{background-position:200% 0}to{background-position:-200% 0}}.sd-preview-pdf-search-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.sd-preview-pdf-search-result{width:100%;display:flex;align-items:flex-start;gap:8px;padding:8px 10px;background:#ffffff0a;border:1px solid transparent;border-radius:6px;color:var(--sd-pdf-fg);cursor:pointer;text-align:left;transition:background .12s,border-color .12s}.sd-preview-pdf-search-result:hover{background:#ffffff12}.sd-preview-pdf-search-result--active{background:var(--sd-pdf-primary-soft);border-color:#005cbb73}.sd-preview-pdf-search-result__snippet{flex:1 1 auto;font-size:12px;line-height:1.4;color:var(--sd-pdf-fg);word-break:break-word}.sd-preview-pdf-search-result__snippet mark{background:var(--sd-pdf-search-hi);color:#000;padding:0 2px;border-radius:2px}.sd-preview-pdf-search-result__page{flex:0 0 auto;font-size:11px;font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted);background:#ffffff0f;padding:2px 6px;border-radius:4px}.sd-preview-pdf-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-pdf-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-pdf-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-primary:hover{filter:brightness(1.1)}.sd-preview-pdf-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-pdf-btn-secondary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:transparent;color:var(--sd-pdf-fg);border:1px solid var(--sd-pdf-divider);border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:background .12s}.sd-preview-pdf-btn-secondary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-secondary:hover{background:#ffffff0f}.sd-preview-pdf-btn-secondary:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3225
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.21", type: SdPreviewPdf, isStandalone: true, selector: "sd-preview-pdf", inputs: { autoIdInput: { classPropertyName: "autoIdInput", publicName: "autoId", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, startPage: { classPropertyName: "startPage", publicName: "startPage", isSignal: true, isRequired: false, transformFunction: null }, initialZoom: { classPropertyName: "initialZoom", publicName: "initialZoom", isSignal: true, isRequired: false, transformFunction: null }, sidebar: { classPropertyName: "sidebar", publicName: "sidebar", isSignal: true, isRequired: false, transformFunction: null }, sidebarOpen: { classPropertyName: "sidebarOpen", publicName: "sidebarOpen", isSignal: true, isRequired: false, transformFunction: null }, scrollMode: { classPropertyName: "scrollMode", publicName: "scrollMode", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, downloadable: { classPropertyName: "downloadable", publicName: "downloadable", isSignal: true, isRequired: false, transformFunction: null }, password: { classPropertyName: "password", publicName: "password", isSignal: true, isRequired: false, transformFunction: null }, httpHeaders: { classPropertyName: "httpHeaders", publicName: "httpHeaders", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { close: "close", loaded: "loaded", pageChange: "pageChange", zoomChange: "zoomChange", download: "download", loadError: "loadError", searchChange: "searchChange" }, host: { attributes: { "tabindex": "0" }, listeners: { "keydown": "onKeyDown($event)" }, properties: { "attr.data-theme": "theme()", "class.sd-preview-pdf-host--fullscreen": "isFullscreen()" }, classAttribute: "sd-preview-pdf-host" }, viewQueries: [{ propertyName: "pageCanvasRef", first: true, predicate: ["pageCanvas"], descendants: true, isSignal: true }, { propertyName: "stageEl", first: true, predicate: ["stageRef"], descendants: true, isSignal: true }, { propertyName: "pageInputRef", first: true, predicate: ["pageInput"], descendants: true, isSignal: true }, { propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "sidebarContentRef", first: true, predicate: ["sidebarContent"], descendants: true, isSignal: true }, { propertyName: "outlineItemRefs", predicate: ["outlineItem"], descendants: true, isSignal: true }, { propertyName: "sidebarTabRefs", predicate: ["sidebarTab"], descendants: true, isSignal: true }, { propertyName: "continuousCanvases", predicate: ["continuousCanvas"], descendants: true, isSignal: true }, { propertyName: "thumbCanvases", predicate: ["thumbCanvas"], descendants: true, isSignal: true }], ngImport: i0, template: "<!--\n sd-preview-pdf shell. Self-contained \u2014 sizes to its parent. Consumer wraps\n in <sd-modal>/<sd-side-drawer>/page section. Header + sidebar + stage +\n floating toolbar mirror Artboards A / C / E from the design handoff.\n-->\n<div class=\"sd-preview-pdf-shell\" role=\"region\">\n @let _stage = stage();\n @let _numPages = numPages();\n @let _activePage = activePage();\n @let _isSidebarOpen = isSidebarOpen();\n @let _searchBarOpen = searchBarOpen();\n @let _searchTerm = searchTerm();\n @let _searchTotal = searchTotal();\n\n <!-- ============================================\n HEADER BAR \u2014 hidden in empty + loading per design\n ============================================ -->\n @if (_stage === 'ready') {\n <div class=\"sd-preview-pdf-header\">\n <div class=\"sd-preview-pdf-header__left\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_isSidebarOpen\"\n [attr.aria-expanded]=\"_isSidebarOpen\"\n [attr.aria-controls]=\"sidebarRegionId()\"\n [attr.aria-label]=\"'core.component.preview-pdf.toggle-sidebar' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.toggle-sidebar' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSidebarToggle()\"\n (click)=\"toggleSidebar()\">\n <sd-icon fontIcon=\"menu\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-header__divider\"></span>\n <div class=\"sd-preview-pdf-fileicon\" aria-hidden=\"true\">PDF</div>\n <div class=\"sd-preview-pdf-header__meta\">\n <div class=\"sd-preview-pdf-header__name\" [title]=\"title() || filename()\">\n {{ title() || filename() }}\n </div>\n <div class=\"sd-preview-pdf-header__sub\">\n {{ 'core.component.preview-pdf.pages-count' | sdTranslate: { count: _numPages } }}\n </div>\n </div>\n </div>\n\n <div class=\"sd-preview-pdf-header__right\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_searchBarOpen\"\n [attr.aria-expanded]=\"_searchBarOpen\"\n [attr.aria-controls]=\"searchBarId()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchToggle()\"\n (click)=\"searchBarOpen() ? closeSearch() : openSearch()\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n </button>\n @if (canPrint()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.print' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.print' | sdTranslate\"\n [attr.data-autoId]=\"autoIdPrint()\"\n (click)=\"printFile()\">\n <sd-icon fontIcon=\"print\"></sd-icon>\n </button>\n }\n @if (canDownload()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.download' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.download' | sdTranslate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n @if (canFullscreen()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.fullscreen' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.fullscreen' | sdTranslate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n }\n </div>\n </div>\n\n <!-- ============================================\n SEARCH BAR \u2014 between header + body, only when bar is open\n ============================================ -->\n <div\n class=\"sd-preview-pdf-searchbar\"\n role=\"search\"\n [id]=\"searchBarId()\"\n [hidden]=\"!_searchBarOpen\"\n [style.display]=\"_searchBarOpen ? null : 'none'\">\n @if (_searchBarOpen) {\n <sd-icon class=\"sd-preview-pdf-searchbar__icon\" fontIcon=\"search\"></sd-icon>\n <input\n #searchInput\n type=\"text\"\n class=\"sd-preview-pdf-searchbar__input\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-placeholder' | sdTranslate\"\n [attr.placeholder]=\"'core.component.preview-pdf.search-placeholder' | sdTranslate\"\n [value]=\"_searchTerm\"\n [attr.data-autoId]=\"autoIdSearchInput()\"\n (input)=\"onSearchInput($event)\" />\n <span class=\"sd-preview-pdf-searchbar__counter\" aria-live=\"polite\">\n {{ searchCurrent() }} / {{ _searchTotal }}{{ searchTruncated() ? '+' : '' }}\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-prev' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search-prev' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchPrev()\"\n (click)=\"searchPrev()\">\n <sd-icon fontIcon=\"keyboard_arrow_up\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-next' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search-next' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchNext()\"\n (click)=\"searchNext()\">\n <sd-icon fontIcon=\"keyboard_arrow_down\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__divider\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchCaseSensitive()\"\n [attr.aria-pressed]=\"searchCaseSensitive()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-case' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search-case' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchCase()\"\n (click)=\"toggleSearchCaseSensitive()\">\n <sd-icon fontIcon=\"match_case\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchWholeWord()\"\n [attr.aria-pressed]=\"searchWholeWord()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-whole' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search-whole' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchWhole()\"\n (click)=\"toggleSearchWholeWord()\">\n <sd-icon fontIcon=\"text_fields\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__spacer\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-close' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search-close' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchClose()\"\n (click)=\"closeSearch()\">\n <sd-icon fontIcon=\"close\"></sd-icon>\n </button>\n }\n </div>\n }\n\n <!-- ============================================\n BODY \u2014 sidebar + stage\n ============================================ -->\n <div class=\"sd-preview-pdf-body\">\n <!-- SIDEBAR ............................................................. -->\n @if (_stage === 'ready') {\n <aside\n class=\"sd-preview-pdf-sidebar\"\n role=\"complementary\"\n [id]=\"sidebarRegionId()\"\n [hidden]=\"!_isSidebarOpen\"\n [style.display]=\"_isSidebarOpen ? null : 'none'\">\n @if (_isSidebarOpen) {\n @let _sidebarMode = sidebarMode();\n <div class=\"sd-preview-pdf-sidebar__tabs\" role=\"tablist\">\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabThumbnailsId()\"\n [attr.aria-selected]=\"_sidebarMode === 'thumbnails'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'thumbnails' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'thumbnails'\"\n data-sidebar-mode=\"thumbnails\"\n [attr.data-autoId]=\"autoIdTabThumbnails()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'thumbnails')\"\n (click)=\"setSidebarMode('thumbnails')\">\n <sd-icon fontIcon=\"view_carousel\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.thumbnails' | sdTranslate }}</span>\n </button>\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabOutlineId()\"\n [attr.aria-selected]=\"_sidebarMode === 'outline'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'outline' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'outline'\"\n data-sidebar-mode=\"outline\"\n [attr.data-autoId]=\"autoIdTabOutline()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'outline')\"\n (click)=\"setSidebarMode('outline')\">\n <sd-icon fontIcon=\"list\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.outline' | sdTranslate }}</span>\n </button>\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabSearchId()\"\n [attr.aria-selected]=\"_sidebarMode === 'search'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'search' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'search'\"\n data-sidebar-mode=\"search\"\n [attr.data-autoId]=\"autoIdTabSearch()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'search')\"\n (click)=\"setSidebarMode('search')\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.search' | sdTranslate }}</span>\n </button>\n </div>\n\n <div\n #sidebarContent\n class=\"sd-preview-pdf-sidebar__content\"\n role=\"tabpanel\"\n [id]=\"sidebarPanelId()\"\n [attr.aria-labelledby]=\"sidebarActiveTabId()\"\n (scroll)=\"onThumbnailSidebarScroll($event)\">\n @switch (_sidebarMode) {\n @case ('thumbnails') {\n <ul class=\"sd-preview-pdf-thumb-list\">\n <li class=\"sd-preview-pdf-thumb-list__spacer\" aria-hidden=\"true\" [style.height.px]=\"thumbnailTopSpacer()\"></li>\n @for (p of pageNumbers(); track trackByPage($index, p)) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-thumb\"\n [class.sd-preview-pdf-thumb--active]=\"p === _activePage\"\n [attr.aria-current]=\"p === _activePage ? 'page' : null\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | sdTranslate: { current: p, total: _numPages }\"\n [attr.data-autoId]=\"autoIdThumb(p - 1)\"\n (click)=\"goToPage(p)\">\n <div class=\"sd-preview-pdf-thumb__page\">\n <canvas #thumbCanvas class=\"sd-preview-pdf-thumb__canvas\" [attr.data-page]=\"p\" aria-hidden=\"true\"></canvas>\n </div>\n <span class=\"sd-preview-pdf-thumb__label\">{{ p }}</span>\n </button>\n </li>\n }\n <li class=\"sd-preview-pdf-thumb-list__spacer\" aria-hidden=\"true\" [style.height.px]=\"thumbnailBottomSpacer()\"></li>\n </ul>\n }\n @case ('outline') {\n <ul\n class=\"sd-preview-pdf-outline-tree\"\n role=\"tree\"\n [attr.aria-label]=\"'core.component.preview-pdf.tab.outline' | sdTranslate\">\n @for (row of visibleOutline(); track row.item.id) {\n <li class=\"sd-preview-pdf-outline-tree__node\" role=\"none\">\n <div class=\"sd-preview-pdf-outline-tree__row\" [style.padding-left.px]=\"(row.level - 1) * 16\">\n @if (row.item.children.length > 0) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-outline-tree__toggle\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.item.title\"\n [attr.aria-expanded]=\"isOutlineExpanded(row.item.id)\"\n (click)=\"toggleOutlineItem(row.item.id, $event)\">\n <sd-icon [fontIcon]=\"isOutlineExpanded(row.item.id) ? 'expand_more' : 'chevron_right'\"></sd-icon>\n </button>\n } @else {\n <span class=\"sd-preview-pdf-outline-tree__toggle-placeholder\" aria-hidden=\"true\"></span>\n }\n\n <!-- why: role=\"treeitem\" B\u1EAET BU\u1ED8C c\u00F3 aria-selected \u2014 thi\u1EBFu n\u00F3 screen reader\n kh\u00F4ng \u0111\u1ECDc \u0111\u01B0\u1EE3c m\u1EE5c outline n\u00E0o \u0111ang l\u00E0 m\u1EE5c hi\u1EC7n h\u00E0nh. \u1EDE \u0111\u00E2y \"\u0111\u01B0\u1EE3c ch\u1ECDn\"\n = m\u1EE5c tr\u1ECF t\u1EDBi trang \u0111ang xem, \u0111\u00FAng b\u1EB1ng \u0111i\u1EC1u ki\u1EC7n c\u1EE7a aria-current. -->\n @if (row.item.url && row.item.page === null) {\n <a\n #outlineItem\n class=\"sd-preview-pdf-outline-tree__item\"\n role=\"treeitem\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n [href]=\"row.item.url\"\n [attr.data-outline-id]=\"row.item.id\"\n [attr.aria-level]=\"row.level\"\n [attr.aria-expanded]=\"row.item.children.length > 0 ? isOutlineExpanded(row.item.id) : null\"\n [attr.aria-selected]=\"row.item.page === _activePage\"\n [attr.aria-current]=\"row.item.page === _activePage ? 'page' : null\"\n [attr.tabindex]=\"outlineFocusId() === row.item.id ? 0 : -1\"\n (focus)=\"focusOutlineItem(row.item.id)\"\n (keydown)=\"onOutlineKeyDown($event, row.item.id)\">\n {{ row.item.title }}\n </a>\n } @else {\n <button\n #outlineItem\n type=\"button\"\n class=\"sd-preview-pdf-outline-tree__item\"\n role=\"treeitem\"\n [attr.data-outline-id]=\"row.item.id\"\n [attr.aria-level]=\"row.level\"\n [attr.aria-expanded]=\"row.item.children.length > 0 ? isOutlineExpanded(row.item.id) : null\"\n [attr.aria-selected]=\"row.item.page === _activePage\"\n [attr.aria-current]=\"row.item.page === _activePage ? 'page' : null\"\n [attr.tabindex]=\"outlineFocusId() === row.item.id ? 0 : -1\"\n (focus)=\"focusOutlineItem(row.item.id)\"\n (click)=\"activateOutlineItem(row.item.id)\"\n (keydown)=\"onOutlineKeyDown($event, row.item.id)\">\n {{ row.item.title }}\n </button>\n }\n </div>\n </li>\n }\n </ul>\n }\n @case ('search') {\n @if (!_searchTerm) {\n <div class=\"sd-preview-pdf-sidebar__message\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-placeholder' | sdTranslate }}</p>\n </div>\n } @else if (_searchTotal === 0) {\n <div class=\"sd-preview-pdf-sidebar__message\">\n <sd-icon fontIcon=\"search_off\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-no-results' | sdTranslate }}</p>\n </div>\n } @else {\n @if (searchTruncated()) {\n <p class=\"sd-preview-pdf-search-limit\" role=\"status\">\n {{ 'core.component.preview-pdf.search-truncated' | sdTranslate: { count: _searchTotal } }}\n </p>\n }\n <ul class=\"sd-preview-pdf-search-list\">\n @for (r of searchResults(); track $index; let i = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-search-result\"\n [class.sd-preview-pdf-search-result--active]=\"i === searchActiveIndex()\"\n [attr.data-autoId]=\"autoIdResult(i)\"\n (click)=\"activateSearchResult(i)\">\n <span class=\"sd-preview-pdf-search-result__snippet\">\n {{ r.before }}<mark>{{ r.term }}</mark\n >{{ r.after }}\n </span>\n <span class=\"sd-preview-pdf-search-result__page\">{{ r.page }}</span>\n </button>\n </li>\n }\n </ul>\n }\n }\n }\n </div>\n }\n </aside>\n }\n\n <!-- STAGE ............................................................... -->\n <div class=\"sd-preview-pdf-stage\" #stageRef (wheel)=\"onWheel($event)\" (scroll)=\"onStageScroll()\">\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--muted\">\n <sd-icon fontIcon=\"picture_as_pdf\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.empty.title' | sdTranslate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.empty.subtitle' | sdTranslate }}\n </div>\n </div>\n }\n @case ('loading') {\n @let _loadPercent = loadPercent();\n <div class=\"sd-preview-pdf-status\">\n <div\n class=\"sd-preview-pdf-spinner\"\n role=\"progressbar\"\n [attr.aria-label]=\"'core.component.preview-pdf.loading' | sdTranslate\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"_loadPercent\"></div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.loading' | sdTranslate }}\n @if (_loadPercent > 0) {\n <span class=\"sd-preview-pdf-progress-pct\">&nbsp;{{ _loadPercent }}%</span>\n }\n </div>\n <div class=\"sd-preview-pdf-progress\">\n <div class=\"sd-preview-pdf-progress__fill\" [style.width.%]=\"_loadPercent\"></div>\n </div>\n </div>\n }\n @case ('error') {\n @if (loadError$(); as err) {\n <div class=\"sd-preview-pdf-status\" role=\"alert\" aria-live=\"assertive\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--danger\">\n <sd-icon fontIcon=\"error\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.title' | sdTranslate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.body' | sdTranslate }}\n </div>\n <div class=\"sd-preview-pdf-status__actions\">\n <button type=\"button\" class=\"sd-preview-pdf-btn-primary\" (click)=\"retryLoad()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.retry' | sdTranslate }}</span>\n </button>\n @if (canDownload()) {\n <button type=\"button\" class=\"sd-preview-pdf-btn-secondary\" (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.download-raw' | sdTranslate }}</span>\n </button>\n }\n </div>\n </div>\n }\n }\n @default {\n @if (scrollModeCurrent() === 'continuous') {\n <div class=\"sd-preview-pdf-continuous-content\">\n <div class=\"sd-preview-pdf-continuous-spacer\" [style.height.px]=\"continuousTopSpacer()\"></div>\n @for (pageNumber of continuousPages(); track pageNumber) {\n <div class=\"sd-preview-pdf-continuous-slot\" [style.min-height.px]=\"continuousPageHeights()[pageNumber]\">\n <canvas\n #continuousCanvas\n class=\"sd-preview-pdf-continuous-page\"\n role=\"img\"\n [attr.data-page]=\"pageNumber\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | sdTranslate: { current: pageNumber, total: _numPages }\">\n </canvas>\n </div>\n }\n <div class=\"sd-preview-pdf-continuous-spacer\" [style.height.px]=\"continuousBottomSpacer()\"></div>\n </div>\n } @else {\n <div class=\"sd-preview-pdf-page-wrap\">\n <canvas\n #pageCanvas\n class=\"sd-preview-pdf-page\"\n role=\"img\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | sdTranslate: { current: _activePage, total: _numPages }\">\n </canvas>\n </div>\n }\n }\n }\n\n <!-- FLOATING TOOLBAR ........................................ -->\n @if (showToolbar() && _stage === 'ready') {\n @let _canPrev = canPrev();\n @let _canNext = canNext();\n @let _zoomMode = zoomMode();\n @let _scrollModeCurrent = scrollModeCurrent();\n <div class=\"sd-preview-pdf-toolbar\" role=\"toolbar\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.first-page' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.first-page' | sdTranslate\"\n [attr.data-autoId]=\"autoIdFirst()\"\n (click)=\"firstPage()\">\n <sd-icon fontIcon=\"first_page\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.prev-page' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.prev-page' | sdTranslate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"prevPage()\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__page\">\n <input\n type=\"text\"\n inputmode=\"numeric\"\n class=\"sd-preview-pdf-toolbar__page-input\"\n [value]=\"_activePage\"\n #pageInput\n (keydown.enter)=\"onPageInputEnter($event)\"\n (blur)=\"onPageInputBlur($event)\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | sdTranslate: { current: _activePage, total: _numPages }\"\n [attr.data-autoId]=\"autoIdPageInput()\" />\n <span class=\"sd-preview-pdf-toolbar__page-total\">/ {{ _numPages }}</span>\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.next-page' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.next-page' | sdTranslate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"nextPage()\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.last-page' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.last-page' | sdTranslate\"\n [attr.data-autoId]=\"autoIdLast()\"\n (click)=\"lastPage()\">\n <sd-icon fontIcon=\"last_page\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-out' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-out' | sdTranslate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"remove\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__zoom\" aria-live=\"polite\" [attr.data-autoId]=\"autoIdZoomReadout()\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-in' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-in' | sdTranslate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"add\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-fit'\"\n [attr.data-autoId]=\"autoIdFitPage()\"\n (click)=\"setZoom('page-fit')\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-page' | sdTranslate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-width'\"\n [attr.data-autoId]=\"autoIdFitWidth()\"\n (click)=\"setZoom('page-width')\">\n <sd-icon fontIcon=\"swap_horiz\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-width' | sdTranslate }}</span>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.rotate' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.rotate' | sdTranslate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [attr.data-autoId]=\"autoIdScrollMode()\"\n (click)=\"setScrollMode(_scrollModeCurrent === 'page' ? 'continuous' : 'page')\">\n <sd-icon [fontIcon]=\"_scrollModeCurrent === 'page' ? 'description' : 'view_day'\"></sd-icon>\n <span>\n {{\n (_scrollModeCurrent === 'page' ? 'core.component.preview-pdf.scroll-page' : 'core.component.preview-pdf.scroll-continuous')\n | sdTranslate\n }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{--sd-pdf-shell-bg: #1a1c1f;--sd-pdf-sidebar-bg: #22252a;--sd-pdf-toolbar-bg: rgba(20, 22, 25, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .4);--sd-pdf-divider: rgba(255, 255, 255, .08);--sd-pdf-fg: #f5f6f7;--sd-pdf-fg-muted: rgba(255, 255, 255, .6);--sd-pdf-fg-subtle: rgba(255, 255, 255, .4);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .18));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .1);--sd-pdf-danger-border: rgba(186, 26, 26, .3);--sd-pdf-danger-tint: rgba(186, 26, 26, .15);--sd-pdf-hover-bg: rgba(255, 255, 255, .1);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(249, 115, 22, .85);display:block;width:100%;height:100%;min-height:320px;outline:none}:host:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}:host([data-theme=light]){--sd-pdf-shell-bg: #f7f8fa;--sd-pdf-sidebar-bg: #ffffff;--sd-pdf-toolbar-bg: rgba(255, 255, 255, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .12);--sd-pdf-divider: rgba(0, 0, 0, .08);--sd-pdf-fg: #1a1c1f;--sd-pdf-fg-muted: rgba(0, 0, 0, .62);--sd-pdf-fg-subtle: rgba(0, 0, 0, .42);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .16));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .08);--sd-pdf-danger-border: rgba(186, 26, 26, .25);--sd-pdf-danger-tint: rgba(186, 26, 26, .12);--sd-pdf-hover-bg: rgba(0, 0, 0, .06);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(250, 204, 21, .9)}:host(.sd-preview-pdf-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-pdf-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-pdf-shell-bg);color:var(--sd-pdf-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-pdf-header{display:flex;align-items:center;justify-content:space-between;gap:12px;height:57px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-pdf-divider);flex:0 0 auto;z-index:2}.sd-preview-pdf-header__left{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 auto}.sd-preview-pdf-header__right{display:flex;align-items:center;gap:4px;flex-shrink:0}.sd-preview-pdf-header__divider{display:inline-block;width:1px;height:20px;background:var(--sd-pdf-divider)}.sd-preview-pdf-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-pdf-header__name{font-size:14px;font-weight:500;color:var(--sd-pdf-fg);max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-pdf-header__sub{font-size:11px;color:var(--sd-pdf-fg-muted);margin-top:2px}.sd-preview-pdf-fileicon{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;background:var(--sd-pdf-danger-tint);border:1px solid var(--sd-pdf-danger-border);color:var(--sd-error-light, #f87171);font:700 10px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-pdf-iconbtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:32px;height:32px;padding:0 8px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;transition:background .12s,color .12s;white-space:nowrap;flex-shrink:0}.sd-preview-pdf-iconbtn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-iconbtn__label{font-size:13px;font-weight:500}.sd-preview-pdf-iconbtn:hover:not(:disabled){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-iconbtn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-iconbtn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-iconbtn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-searchbar{display:flex;align-items:center;gap:6px;height:46px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);border-bottom:1px solid var(--sd-pdf-divider);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);flex:0 0 auto;z-index:2}.sd-preview-pdf-searchbar__icon{color:var(--sd-pdf-fg-muted);font-size:18px;width:18px;height:18px;flex-shrink:0}.sd-preview-pdf-searchbar__input{flex:0 1 360px;min-width:160px;height:30px;background:#ffffff0f;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:6px;padding:0 10px;font-size:13px;outline:none}.sd-preview-pdf-searchbar__input::placeholder{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-searchbar__input:focus{border-color:var(--sd-pdf-primary);background:#ffffff17}.sd-preview-pdf-searchbar__counter{color:var(--sd-pdf-fg-muted);font-size:12px;font-variant-numeric:tabular-nums;min-width:48px;text-align:center;flex-shrink:0}.sd-preview-pdf-searchbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);flex-shrink:0}.sd-preview-pdf-searchbar__spacer{flex:1 1 auto}.sd-pdf-search-overlay{position:absolute;top:8px;left:50%;transform:translate(-50%);display:flex;flex-wrap:wrap;gap:4px;max-width:80%;pointer-events:none;z-index:5}.sd-pdf-search-hi{display:inline-block;padding:1px 4px;border-radius:2px;background:var(--sd-pdf-search-hi);color:#000;font-size:12px;font-weight:500}.sd-pdf-search-hi--active{background:var(--sd-pdf-search-hi-active);color:#fff;box-shadow:0 0 0 2px #fff6}.sd-preview-pdf-body{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:row;overflow:hidden}.sd-preview-pdf-sidebar{width:240px;display:flex;flex-direction:column;background:var(--sd-pdf-sidebar-bg);border-right:1px solid var(--sd-pdf-divider);flex-shrink:0;min-height:0}.sd-preview-pdf-sidebar__tabs{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-sidebar__tab{flex:1 1 0;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;padding:0 4px;background:transparent;border:none;color:var(--sd-pdf-fg-muted);border-radius:6px;cursor:pointer;font-size:12px;font-weight:500;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-sidebar__tab sd-icon{font-size:14px;width:14px;height:14px}.sd-preview-pdf-sidebar__tab:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-sidebar__tab--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-sidebar__content{flex:1 1 auto;overflow:auto;padding:12px}.sd-preview-pdf-sidebar__message{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 16px;text-align:center;color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-sidebar__message sd-icon{font-size:28px;width:28px;height:28px;opacity:.6}.sd-preview-pdf-sidebar__message p{margin:0;font-size:12px;line-height:1.5}.sd-preview-pdf-outline-tree{list-style:none;margin:0;padding:0}.sd-preview-pdf-outline-tree__node{margin:0;padding:0}.sd-preview-pdf-outline-tree__row{display:flex;align-items:center;min-width:0}.sd-preview-pdf-outline-tree__toggle,.sd-preview-pdf-outline-tree__toggle-placeholder{width:24px;height:28px;flex:0 0 24px}.sd-preview-pdf-outline-tree__toggle{display:inline-flex;align-items:center;justify-content:center;padding:0;border:0;border-radius:4px;background:transparent;color:var(--sd-pdf-fg-muted);cursor:pointer}.sd-preview-pdf-outline-tree__toggle:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-outline-tree__toggle sd-icon{width:16px;height:16px;font-size:16px}.sd-preview-pdf-outline-tree__item{min-width:0;flex:1 1 auto;overflow:hidden;padding:6px 8px;border:0;border-radius:5px;background:transparent;color:var(--sd-pdf-fg-muted);font:inherit;font-size:12px;line-height:1.35;text-align:left;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.sd-preview-pdf-outline-tree__item:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-outline-tree__item[aria-current=page]{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary);font-weight:600}.sd-preview-pdf-outline-tree__item:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:-2px}.sd-preview-pdf-thumb-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.sd-preview-pdf-thumb-list__spacer{flex:0 0 auto;pointer-events:none}.sd-preview-pdf-search-limit{margin:0 0 8px;color:var(--sd-pdf-fg-muted);font-size:11px;line-height:1.4}.sd-preview-pdf-thumb{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;padding:0;background:transparent;border:none;cursor:pointer}.sd-preview-pdf-thumb__page{width:152px;aspect-ratio:612/792;background:var(--sd-pdf-page-bg);border-radius:2px;outline:1px solid rgba(255,255,255,.08);outline-offset:-1px;box-shadow:0 2px 6px #0000004d;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-user-select:none;user-select:none;background-image:linear-gradient(90deg,#0000,#0000000a,#0000);background-size:200% 100%;animation:sd-preview-pdf-thumb-skel 1.6s ease-in-out infinite}.sd-preview-pdf-thumb__canvas{display:block;max-width:100%;max-height:100%;background:var(--sd-pdf-page-bg)}.sd-preview-pdf-thumb__label{font-size:11px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:-2px}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__label{color:var(--sd-pdf-primary);font-weight:600}.sd-preview-pdf-thumb:focus-visible .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-stage{position:relative;flex:1 1 auto;min-width:0;min-height:0;background:var(--sd-pdf-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.015) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;overflow:auto;padding:32px 24px 96px;display:flex;justify-content:center;align-items:flex-start}:host([data-theme=light]) .sd-preview-pdf-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-pdf-page-wrap{display:inline-block;position:relative}.sd-preview-pdf-page{display:block;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow);max-width:100%}.sd-preview-pdf-continuous-content{width:100%;min-height:100%}.sd-preview-pdf-continuous-spacer{width:1px}.sd-preview-pdf-continuous-slot{display:flex;justify-content:center;align-items:flex-start}.sd-preview-pdf-continuous-slot+.sd-preview-pdf-continuous-slot{margin-top:24px}.sd-preview-pdf-continuous-page{display:block;max-width:100%;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow)}.sd-preview-pdf-toolbar{position:absolute;bottom:20px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:2px;padding:5px 6px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-pdf-divider);border-radius:10px;box-shadow:0 8px 30px #0006;z-index:4;max-width:calc(100% - 32px);overflow-x:auto}.sd-preview-pdf-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:30px;height:28px;padding:0 6px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-toolbar__btn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-toolbar__btn span{font-size:13px;font-weight:500}.sd-preview-pdf-toolbar__btn--with-label{padding:0 10px}.sd-preview-pdf-toolbar__btn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__btn:hover:not(:disabled):not(.sd-preview-pdf-toolbar__btn--active){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-toolbar__btn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-toolbar__btn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-toolbar__page{display:inline-flex;align-items:center;gap:4px;padding:0 6px;flex-shrink:0}.sd-preview-pdf-toolbar__page-input{width:36px;height:24px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:4px;text-align:center;font-size:13px;font-variant-numeric:tabular-nums;outline:none}.sd-preview-pdf-toolbar__page-input:focus{border-color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__page-total{font-size:13px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-toolbar__zoom{min-width:56px;text-align:center;color:var(--sd-pdf-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums;background:#ffffff14;border-radius:4px;padding:2px 8px}.sd-preview-pdf-toolbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);margin:0 4px;flex-shrink:0}.sd-preview-pdf-status{margin:auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:32px;max-width:420px}.sd-preview-pdf-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-status__tile sd-icon{font-size:48px;width:48px;height:48px}.sd-preview-pdf-status__tile--muted{background:#ffffff0d}.sd-preview-pdf-status__tile--muted sd-icon{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-status__tile--danger{background:var(--sd-pdf-danger-soft);border-color:var(--sd-pdf-danger-border)}.sd-preview-pdf-status__tile--danger sd-icon{color:var(--sd-pdf-danger)}.sd-preview-pdf-status__title{font-size:16px;font-weight:500;color:var(--sd-pdf-fg)}.sd-preview-pdf-status__hint{font-size:13px;color:var(--sd-pdf-fg-muted);line-height:1.5}.sd-preview-pdf-status__actions{display:flex;gap:8px;margin-top:4px}.sd-preview-pdf-progress-pct{font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted)}.sd-preview-pdf-progress{width:240px;height:4px;background:#ffffff14;border-radius:999px;overflow:hidden}.sd-preview-pdf-progress__fill{height:100%;background:var(--sd-pdf-primary);transition:width .12s linear}.sd-preview-pdf-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-pdf-primary);animation:sd-preview-pdf-spin .9s linear infinite}@keyframes sd-preview-pdf-spin{to{transform:rotate(360deg)}}@keyframes sd-preview-pdf-thumb-skel{0%{background-position:200% 0}to{background-position:-200% 0}}.sd-preview-pdf-search-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.sd-preview-pdf-search-result{width:100%;display:flex;align-items:flex-start;gap:8px;padding:8px 10px;background:#ffffff0a;border:1px solid transparent;border-radius:6px;color:var(--sd-pdf-fg);cursor:pointer;text-align:left;transition:background .12s,border-color .12s}.sd-preview-pdf-search-result:hover{background:#ffffff12}.sd-preview-pdf-search-result--active{background:var(--sd-pdf-primary-soft);border-color:#005cbb73}.sd-preview-pdf-search-result__snippet{flex:1 1 auto;font-size:12px;line-height:1.4;color:var(--sd-pdf-fg);word-break:break-word}.sd-preview-pdf-search-result__snippet mark{background:var(--sd-pdf-search-hi);color:#000;padding:0 2px;border-radius:2px}.sd-preview-pdf-search-result__page{flex:0 0 auto;font-size:11px;font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted);background:#ffffff0f;padding:2px 6px;border-radius:4px}.sd-preview-pdf-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-pdf-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-pdf-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-primary:hover{filter:brightness(1.1)}.sd-preview-pdf-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-pdf-btn-secondary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:transparent;color:var(--sd-pdf-fg);border:1px solid var(--sd-pdf-divider);border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:background .12s}.sd-preview-pdf-btn-secondary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-secondary:hover{background:#ffffff0f}.sd-preview-pdf-btn-secondary:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}\n"], dependencies: [{ kind: "component", type: SdIcon, selector: "sd-icon", inputs: ["name", "fontIcon", "color", "set", "fontSet", "size", "strokeWidth", "absoluteStrokeWidth", "ariaLabel"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "pipe", type: SdTranslatePipe, name: "sdTranslate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
3216
3226
  }
3217
3227
  _a = SdPreviewPdf;
3218
3228
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdPreviewPdf, decorators: [{
3219
3229
  type: Component,
3220
- args: [{ selector: 'sd-preview-pdf', standalone: true, imports: [SdIcon, CommonModule, FormsModule, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
3230
+ args: [{ selector: 'sd-preview-pdf', standalone: true, imports: [SdIcon, CommonModule, FormsModule, SdTranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, host: {
3221
3231
  tabindex: '0',
3222
3232
  class: 'sd-preview-pdf-host',
3223
3233
  // WHY data-theme attribute: SCSS uses :host([data-theme="light"]) to swap
@@ -3225,7 +3235,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImpo
3225
3235
  // collision with consumer styling.
3226
3236
  '[attr.data-theme]': 'theme()',
3227
3237
  '[class.sd-preview-pdf-host--fullscreen]': 'isFullscreen()',
3228
- }, template: "<!--\n sd-preview-pdf shell. Self-contained \u2014 sizes to its parent. Consumer wraps\n in <sd-modal>/<sd-side-drawer>/page section. Header + sidebar + stage +\n floating toolbar mirror Artboards A / C / E from the design handoff.\n-->\n<div class=\"sd-preview-pdf-shell\" role=\"region\">\n @let _stage = stage();\n @let _numPages = numPages();\n @let _activePage = activePage();\n @let _isSidebarOpen = isSidebarOpen();\n @let _searchBarOpen = searchBarOpen();\n @let _searchTerm = searchTerm();\n @let _searchTotal = searchTotal();\n\n <!-- ============================================\n HEADER BAR \u2014 hidden in empty + loading per design\n ============================================ -->\n @if (_stage === 'ready') {\n <div class=\"sd-preview-pdf-header\">\n <div class=\"sd-preview-pdf-header__left\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_isSidebarOpen\"\n [attr.aria-expanded]=\"_isSidebarOpen\"\n [attr.aria-controls]=\"sidebarRegionId()\"\n [attr.aria-label]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.title]=\"'core.component.preview-pdf.toggle-sidebar' | translate\"\n [attr.data-autoId]=\"autoIdSidebarToggle()\"\n (click)=\"toggleSidebar()\">\n <sd-icon fontIcon=\"menu\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-header__divider\"></span>\n <div class=\"sd-preview-pdf-fileicon\" aria-hidden=\"true\">PDF</div>\n <div class=\"sd-preview-pdf-header__meta\">\n <div class=\"sd-preview-pdf-header__name\" [title]=\"title() || filename()\">\n {{ title() || filename() }}\n </div>\n <div class=\"sd-preview-pdf-header__sub\">\n {{ 'core.component.preview-pdf.pages-count' | translate: { count: _numPages } }}\n </div>\n </div>\n </div>\n\n <div class=\"sd-preview-pdf-header__right\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_searchBarOpen\"\n [attr.aria-expanded]=\"_searchBarOpen\"\n [attr.aria-controls]=\"searchBarId()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search' | translate\"\n [attr.data-autoId]=\"autoIdSearchToggle()\"\n (click)=\"searchBarOpen() ? closeSearch() : openSearch()\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n </button>\n @if (canPrint()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.print' | translate\"\n [attr.title]=\"'core.component.preview-pdf.print' | translate\"\n [attr.data-autoId]=\"autoIdPrint()\"\n (click)=\"printFile()\">\n <sd-icon fontIcon=\"print\"></sd-icon>\n </button>\n }\n @if (canDownload()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.download' | translate\"\n [attr.title]=\"'core.component.preview-pdf.download' | translate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n @if (canFullscreen()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.title]=\"'core.component.preview-pdf.fullscreen' | translate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n }\n </div>\n </div>\n\n <!-- ============================================\n SEARCH BAR \u2014 between header + body, only when bar is open\n ============================================ -->\n <div\n class=\"sd-preview-pdf-searchbar\"\n role=\"search\"\n [id]=\"searchBarId()\"\n [hidden]=\"!_searchBarOpen\"\n [style.display]=\"_searchBarOpen ? null : 'none'\">\n @if (_searchBarOpen) {\n <sd-icon class=\"sd-preview-pdf-searchbar__icon\" fontIcon=\"search\"></sd-icon>\n <input\n #searchInput\n type=\"text\"\n class=\"sd-preview-pdf-searchbar__input\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-placeholder' | translate\"\n [attr.placeholder]=\"'core.component.preview-pdf.search-placeholder' | translate\"\n [value]=\"_searchTerm\"\n [attr.data-autoId]=\"autoIdSearchInput()\"\n (input)=\"onSearchInput($event)\" />\n <span class=\"sd-preview-pdf-searchbar__counter\" aria-live=\"polite\">\n {{ searchCurrent() }} / {{ _searchTotal }}{{ searchTruncated() ? '+' : '' }}\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-prev' | translate\"\n [attr.data-autoId]=\"autoIdSearchPrev()\"\n (click)=\"searchPrev()\">\n <sd-icon fontIcon=\"keyboard_arrow_up\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-next' | translate\"\n [attr.data-autoId]=\"autoIdSearchNext()\"\n (click)=\"searchNext()\">\n <sd-icon fontIcon=\"keyboard_arrow_down\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__divider\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchCaseSensitive()\"\n [attr.aria-pressed]=\"searchCaseSensitive()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-case' | translate\"\n [attr.data-autoId]=\"autoIdSearchCase()\"\n (click)=\"toggleSearchCaseSensitive()\">\n <sd-icon fontIcon=\"match_case\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchWholeWord()\"\n [attr.aria-pressed]=\"searchWholeWord()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-whole' | translate\"\n [attr.data-autoId]=\"autoIdSearchWhole()\"\n (click)=\"toggleSearchWholeWord()\">\n <sd-icon fontIcon=\"text_fields\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__spacer\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.title]=\"'core.component.preview-pdf.search-close' | translate\"\n [attr.data-autoId]=\"autoIdSearchClose()\"\n (click)=\"closeSearch()\">\n <sd-icon fontIcon=\"close\"></sd-icon>\n </button>\n }\n </div>\n }\n\n <!-- ============================================\n BODY \u2014 sidebar + stage\n ============================================ -->\n <div class=\"sd-preview-pdf-body\">\n <!-- SIDEBAR ............................................................. -->\n @if (_stage === 'ready') {\n <aside\n class=\"sd-preview-pdf-sidebar\"\n role=\"complementary\"\n [id]=\"sidebarRegionId()\"\n [hidden]=\"!_isSidebarOpen\"\n [style.display]=\"_isSidebarOpen ? null : 'none'\">\n @if (_isSidebarOpen) {\n @let _sidebarMode = sidebarMode();\n <div class=\"sd-preview-pdf-sidebar__tabs\" role=\"tablist\">\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabThumbnailsId()\"\n [attr.aria-selected]=\"_sidebarMode === 'thumbnails'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'thumbnails' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'thumbnails'\"\n data-sidebar-mode=\"thumbnails\"\n [attr.data-autoId]=\"autoIdTabThumbnails()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'thumbnails')\"\n (click)=\"setSidebarMode('thumbnails')\">\n <sd-icon fontIcon=\"view_carousel\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.thumbnails' | translate }}</span>\n </button>\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabOutlineId()\"\n [attr.aria-selected]=\"_sidebarMode === 'outline'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'outline' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'outline'\"\n data-sidebar-mode=\"outline\"\n [attr.data-autoId]=\"autoIdTabOutline()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'outline')\"\n (click)=\"setSidebarMode('outline')\">\n <sd-icon fontIcon=\"list\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.outline' | translate }}</span>\n </button>\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabSearchId()\"\n [attr.aria-selected]=\"_sidebarMode === 'search'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'search' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'search'\"\n data-sidebar-mode=\"search\"\n [attr.data-autoId]=\"autoIdTabSearch()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'search')\"\n (click)=\"setSidebarMode('search')\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.search' | translate }}</span>\n </button>\n </div>\n\n <div\n #sidebarContent\n class=\"sd-preview-pdf-sidebar__content\"\n role=\"tabpanel\"\n [id]=\"sidebarPanelId()\"\n [attr.aria-labelledby]=\"sidebarActiveTabId()\"\n (scroll)=\"onThumbnailSidebarScroll($event)\">\n @switch (_sidebarMode) {\n @case ('thumbnails') {\n <ul class=\"sd-preview-pdf-thumb-list\">\n <li class=\"sd-preview-pdf-thumb-list__spacer\" aria-hidden=\"true\" [style.height.px]=\"thumbnailTopSpacer()\"></li>\n @for (p of pageNumbers(); track trackByPage($index, p)) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-thumb\"\n [class.sd-preview-pdf-thumb--active]=\"p === _activePage\"\n [attr.aria-current]=\"p === _activePage ? 'page' : null\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: p, total: _numPages }\"\n [attr.data-autoId]=\"autoIdThumb(p - 1)\"\n (click)=\"goToPage(p)\">\n <div class=\"sd-preview-pdf-thumb__page\">\n <canvas #thumbCanvas class=\"sd-preview-pdf-thumb__canvas\" [attr.data-page]=\"p\" aria-hidden=\"true\"></canvas>\n </div>\n <span class=\"sd-preview-pdf-thumb__label\">{{ p }}</span>\n </button>\n </li>\n }\n <li class=\"sd-preview-pdf-thumb-list__spacer\" aria-hidden=\"true\" [style.height.px]=\"thumbnailBottomSpacer()\"></li>\n </ul>\n }\n @case ('outline') {\n <ul\n class=\"sd-preview-pdf-outline-tree\"\n role=\"tree\"\n [attr.aria-label]=\"'core.component.preview-pdf.tab.outline' | translate\">\n @for (row of visibleOutline(); track row.item.id) {\n <li class=\"sd-preview-pdf-outline-tree__node\" role=\"none\">\n <div class=\"sd-preview-pdf-outline-tree__row\" [style.padding-left.px]=\"(row.level - 1) * 16\">\n @if (row.item.children.length > 0) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-outline-tree__toggle\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.item.title\"\n [attr.aria-expanded]=\"isOutlineExpanded(row.item.id)\"\n (click)=\"toggleOutlineItem(row.item.id, $event)\">\n <sd-icon [fontIcon]=\"isOutlineExpanded(row.item.id) ? 'expand_more' : 'chevron_right'\"></sd-icon>\n </button>\n } @else {\n <span class=\"sd-preview-pdf-outline-tree__toggle-placeholder\" aria-hidden=\"true\"></span>\n }\n\n @if (row.item.url && row.item.page === null) {\n <a\n #outlineItem\n class=\"sd-preview-pdf-outline-tree__item\"\n role=\"treeitem\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n [href]=\"row.item.url\"\n [attr.data-outline-id]=\"row.item.id\"\n [attr.aria-level]=\"row.level\"\n [attr.aria-expanded]=\"row.item.children.length > 0 ? isOutlineExpanded(row.item.id) : null\"\n [attr.aria-current]=\"row.item.page === _activePage ? 'page' : null\"\n [attr.tabindex]=\"outlineFocusId() === row.item.id ? 0 : -1\"\n (focus)=\"focusOutlineItem(row.item.id)\"\n (keydown)=\"onOutlineKeyDown($event, row.item.id)\">\n {{ row.item.title }}\n </a>\n } @else {\n <button\n #outlineItem\n type=\"button\"\n class=\"sd-preview-pdf-outline-tree__item\"\n role=\"treeitem\"\n [attr.data-outline-id]=\"row.item.id\"\n [attr.aria-level]=\"row.level\"\n [attr.aria-expanded]=\"row.item.children.length > 0 ? isOutlineExpanded(row.item.id) : null\"\n [attr.aria-current]=\"row.item.page === _activePage ? 'page' : null\"\n [attr.tabindex]=\"outlineFocusId() === row.item.id ? 0 : -1\"\n (focus)=\"focusOutlineItem(row.item.id)\"\n (click)=\"activateOutlineItem(row.item.id)\"\n (keydown)=\"onOutlineKeyDown($event, row.item.id)\">\n {{ row.item.title }}\n </button>\n }\n </div>\n </li>\n }\n </ul>\n }\n @case ('search') {\n @if (!_searchTerm) {\n <div class=\"sd-preview-pdf-sidebar__message\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-placeholder' | translate }}</p>\n </div>\n } @else if (_searchTotal === 0) {\n <div class=\"sd-preview-pdf-sidebar__message\">\n <sd-icon fontIcon=\"search_off\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-no-results' | translate }}</p>\n </div>\n } @else {\n @if (searchTruncated()) {\n <p class=\"sd-preview-pdf-search-limit\" role=\"status\">\n {{ 'core.component.preview-pdf.search-truncated' | translate: { count: _searchTotal } }}\n </p>\n }\n <ul class=\"sd-preview-pdf-search-list\">\n @for (r of searchResults(); track $index; let i = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-search-result\"\n [class.sd-preview-pdf-search-result--active]=\"i === searchActiveIndex()\"\n [attr.data-autoId]=\"autoIdResult(i)\"\n (click)=\"activateSearchResult(i)\">\n <span class=\"sd-preview-pdf-search-result__snippet\">\n {{ r.before }}<mark>{{ r.term }}</mark\n >{{ r.after }}\n </span>\n <span class=\"sd-preview-pdf-search-result__page\">{{ r.page }}</span>\n </button>\n </li>\n }\n </ul>\n }\n }\n }\n </div>\n }\n </aside>\n }\n\n <!-- STAGE ............................................................... -->\n <div class=\"sd-preview-pdf-stage\" #stageRef (wheel)=\"onWheel($event)\" (scroll)=\"onStageScroll()\">\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--muted\">\n <sd-icon fontIcon=\"picture_as_pdf\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.empty.title' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.empty.subtitle' | translate }}\n </div>\n </div>\n }\n @case ('loading') {\n @let _loadPercent = loadPercent();\n <div class=\"sd-preview-pdf-status\">\n <div\n class=\"sd-preview-pdf-spinner\"\n role=\"progressbar\"\n [attr.aria-label]=\"'core.component.preview-pdf.loading' | translate\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"_loadPercent\"></div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.loading' | translate }}\n @if (_loadPercent > 0) {\n <span class=\"sd-preview-pdf-progress-pct\">&nbsp;{{ _loadPercent }}%</span>\n }\n </div>\n <div class=\"sd-preview-pdf-progress\">\n <div class=\"sd-preview-pdf-progress__fill\" [style.width.%]=\"_loadPercent\"></div>\n </div>\n </div>\n }\n @case ('error') {\n @if (loadError$(); as err) {\n <div class=\"sd-preview-pdf-status\" role=\"alert\" aria-live=\"assertive\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--danger\">\n <sd-icon fontIcon=\"error\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.title' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.body' | translate }}\n </div>\n <div class=\"sd-preview-pdf-status__actions\">\n <button type=\"button\" class=\"sd-preview-pdf-btn-primary\" (click)=\"retryLoad()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.retry' | translate }}</span>\n </button>\n @if (canDownload()) {\n <button type=\"button\" class=\"sd-preview-pdf-btn-secondary\" (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.download-raw' | translate }}</span>\n </button>\n }\n </div>\n </div>\n }\n }\n @default {\n @if (scrollModeCurrent() === 'continuous') {\n <div class=\"sd-preview-pdf-continuous-content\">\n <div class=\"sd-preview-pdf-continuous-spacer\" [style.height.px]=\"continuousTopSpacer()\"></div>\n @for (pageNumber of continuousPages(); track pageNumber) {\n <div class=\"sd-preview-pdf-continuous-slot\" [style.min-height.px]=\"continuousPageHeights()[pageNumber]\">\n <canvas\n #continuousCanvas\n class=\"sd-preview-pdf-continuous-page\"\n role=\"img\"\n [attr.data-page]=\"pageNumber\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: pageNumber, total: _numPages }\">\n </canvas>\n </div>\n }\n <div class=\"sd-preview-pdf-continuous-spacer\" [style.height.px]=\"continuousBottomSpacer()\"></div>\n </div>\n } @else {\n <div class=\"sd-preview-pdf-page-wrap\">\n <canvas\n #pageCanvas\n class=\"sd-preview-pdf-page\"\n role=\"img\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages }\">\n </canvas>\n </div>\n }\n }\n }\n\n <!-- FLOATING TOOLBAR ........................................ -->\n @if (showToolbar() && _stage === 'ready') {\n @let _canPrev = canPrev();\n @let _canNext = canNext();\n @let _zoomMode = zoomMode();\n @let _scrollModeCurrent = scrollModeCurrent();\n <div class=\"sd-preview-pdf-toolbar\" role=\"toolbar\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.first-page' | translate\"\n [attr.data-autoId]=\"autoIdFirst()\"\n (click)=\"firstPage()\">\n <sd-icon fontIcon=\"first_page\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.prev-page' | translate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"prevPage()\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__page\">\n <input\n type=\"text\"\n inputmode=\"numeric\"\n class=\"sd-preview-pdf-toolbar__page-input\"\n [value]=\"_activePage\"\n #pageInput\n (keydown.enter)=\"onPageInputEnter($event)\"\n (blur)=\"onPageInputBlur($event)\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | translate: { current: _activePage, total: _numPages }\"\n [attr.data-autoId]=\"autoIdPageInput()\" />\n <span class=\"sd-preview-pdf-toolbar__page-total\">/ {{ _numPages }}</span>\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.next-page' | translate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"nextPage()\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.title]=\"'core.component.preview-pdf.last-page' | translate\"\n [attr.data-autoId]=\"autoIdLast()\"\n (click)=\"lastPage()\">\n <sd-icon fontIcon=\"last_page\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-out' | translate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"remove\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__zoom\" aria-live=\"polite\" [attr.data-autoId]=\"autoIdZoomReadout()\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-in' | translate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"add\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-fit'\"\n [attr.data-autoId]=\"autoIdFitPage()\"\n (click)=\"setZoom('page-fit')\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-page' | translate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-width'\"\n [attr.data-autoId]=\"autoIdFitWidth()\"\n (click)=\"setZoom('page-width')\">\n <sd-icon fontIcon=\"swap_horiz\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-width' | translate }}</span>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.title]=\"'core.component.preview-pdf.rotate' | translate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [attr.data-autoId]=\"autoIdScrollMode()\"\n (click)=\"setScrollMode(_scrollModeCurrent === 'page' ? 'continuous' : 'page')\">\n <sd-icon [fontIcon]=\"_scrollModeCurrent === 'page' ? 'description' : 'view_day'\"></sd-icon>\n <span>\n {{\n (_scrollModeCurrent === 'page' ? 'core.component.preview-pdf.scroll-page' : 'core.component.preview-pdf.scroll-continuous')\n | translate\n }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{--sd-pdf-shell-bg: #1a1c1f;--sd-pdf-sidebar-bg: #22252a;--sd-pdf-toolbar-bg: rgba(20, 22, 25, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .4);--sd-pdf-divider: rgba(255, 255, 255, .08);--sd-pdf-fg: #f5f6f7;--sd-pdf-fg-muted: rgba(255, 255, 255, .6);--sd-pdf-fg-subtle: rgba(255, 255, 255, .4);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .18));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .1);--sd-pdf-danger-border: rgba(186, 26, 26, .3);--sd-pdf-danger-tint: rgba(186, 26, 26, .15);--sd-pdf-hover-bg: rgba(255, 255, 255, .1);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(249, 115, 22, .85);display:block;width:100%;height:100%;min-height:320px;outline:none}:host:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}:host([data-theme=light]){--sd-pdf-shell-bg: #f7f8fa;--sd-pdf-sidebar-bg: #ffffff;--sd-pdf-toolbar-bg: rgba(255, 255, 255, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .12);--sd-pdf-divider: rgba(0, 0, 0, .08);--sd-pdf-fg: #1a1c1f;--sd-pdf-fg-muted: rgba(0, 0, 0, .62);--sd-pdf-fg-subtle: rgba(0, 0, 0, .42);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .16));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .08);--sd-pdf-danger-border: rgba(186, 26, 26, .25);--sd-pdf-danger-tint: rgba(186, 26, 26, .12);--sd-pdf-hover-bg: rgba(0, 0, 0, .06);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(250, 204, 21, .9)}:host(.sd-preview-pdf-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-pdf-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-pdf-shell-bg);color:var(--sd-pdf-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-pdf-header{display:flex;align-items:center;justify-content:space-between;gap:12px;height:57px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-pdf-divider);flex:0 0 auto;z-index:2}.sd-preview-pdf-header__left{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 auto}.sd-preview-pdf-header__right{display:flex;align-items:center;gap:4px;flex-shrink:0}.sd-preview-pdf-header__divider{display:inline-block;width:1px;height:20px;background:var(--sd-pdf-divider)}.sd-preview-pdf-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-pdf-header__name{font-size:14px;font-weight:500;color:var(--sd-pdf-fg);max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-pdf-header__sub{font-size:11px;color:var(--sd-pdf-fg-muted);margin-top:2px}.sd-preview-pdf-fileicon{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;background:var(--sd-pdf-danger-tint);border:1px solid var(--sd-pdf-danger-border);color:var(--sd-error-light, #f87171);font:700 10px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-pdf-iconbtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:32px;height:32px;padding:0 8px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;transition:background .12s,color .12s;white-space:nowrap;flex-shrink:0}.sd-preview-pdf-iconbtn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-iconbtn__label{font-size:13px;font-weight:500}.sd-preview-pdf-iconbtn:hover:not(:disabled){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-iconbtn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-iconbtn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-iconbtn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-searchbar{display:flex;align-items:center;gap:6px;height:46px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);border-bottom:1px solid var(--sd-pdf-divider);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);flex:0 0 auto;z-index:2}.sd-preview-pdf-searchbar__icon{color:var(--sd-pdf-fg-muted);font-size:18px;width:18px;height:18px;flex-shrink:0}.sd-preview-pdf-searchbar__input{flex:0 1 360px;min-width:160px;height:30px;background:#ffffff0f;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:6px;padding:0 10px;font-size:13px;outline:none}.sd-preview-pdf-searchbar__input::placeholder{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-searchbar__input:focus{border-color:var(--sd-pdf-primary);background:#ffffff17}.sd-preview-pdf-searchbar__counter{color:var(--sd-pdf-fg-muted);font-size:12px;font-variant-numeric:tabular-nums;min-width:48px;text-align:center;flex-shrink:0}.sd-preview-pdf-searchbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);flex-shrink:0}.sd-preview-pdf-searchbar__spacer{flex:1 1 auto}.sd-pdf-search-overlay{position:absolute;top:8px;left:50%;transform:translate(-50%);display:flex;flex-wrap:wrap;gap:4px;max-width:80%;pointer-events:none;z-index:5}.sd-pdf-search-hi{display:inline-block;padding:1px 4px;border-radius:2px;background:var(--sd-pdf-search-hi);color:#000;font-size:12px;font-weight:500}.sd-pdf-search-hi--active{background:var(--sd-pdf-search-hi-active);color:#fff;box-shadow:0 0 0 2px #fff6}.sd-preview-pdf-body{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:row;overflow:hidden}.sd-preview-pdf-sidebar{width:240px;display:flex;flex-direction:column;background:var(--sd-pdf-sidebar-bg);border-right:1px solid var(--sd-pdf-divider);flex-shrink:0;min-height:0}.sd-preview-pdf-sidebar__tabs{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-sidebar__tab{flex:1 1 0;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;padding:0 4px;background:transparent;border:none;color:var(--sd-pdf-fg-muted);border-radius:6px;cursor:pointer;font-size:12px;font-weight:500;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-sidebar__tab sd-icon{font-size:14px;width:14px;height:14px}.sd-preview-pdf-sidebar__tab:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-sidebar__tab--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-sidebar__content{flex:1 1 auto;overflow:auto;padding:12px}.sd-preview-pdf-sidebar__message{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 16px;text-align:center;color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-sidebar__message sd-icon{font-size:28px;width:28px;height:28px;opacity:.6}.sd-preview-pdf-sidebar__message p{margin:0;font-size:12px;line-height:1.5}.sd-preview-pdf-outline-tree{list-style:none;margin:0;padding:0}.sd-preview-pdf-outline-tree__node{margin:0;padding:0}.sd-preview-pdf-outline-tree__row{display:flex;align-items:center;min-width:0}.sd-preview-pdf-outline-tree__toggle,.sd-preview-pdf-outline-tree__toggle-placeholder{width:24px;height:28px;flex:0 0 24px}.sd-preview-pdf-outline-tree__toggle{display:inline-flex;align-items:center;justify-content:center;padding:0;border:0;border-radius:4px;background:transparent;color:var(--sd-pdf-fg-muted);cursor:pointer}.sd-preview-pdf-outline-tree__toggle:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-outline-tree__toggle sd-icon{width:16px;height:16px;font-size:16px}.sd-preview-pdf-outline-tree__item{min-width:0;flex:1 1 auto;overflow:hidden;padding:6px 8px;border:0;border-radius:5px;background:transparent;color:var(--sd-pdf-fg-muted);font:inherit;font-size:12px;line-height:1.35;text-align:left;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.sd-preview-pdf-outline-tree__item:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-outline-tree__item[aria-current=page]{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary);font-weight:600}.sd-preview-pdf-outline-tree__item:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:-2px}.sd-preview-pdf-thumb-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.sd-preview-pdf-thumb-list__spacer{flex:0 0 auto;pointer-events:none}.sd-preview-pdf-search-limit{margin:0 0 8px;color:var(--sd-pdf-fg-muted);font-size:11px;line-height:1.4}.sd-preview-pdf-thumb{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;padding:0;background:transparent;border:none;cursor:pointer}.sd-preview-pdf-thumb__page{width:152px;aspect-ratio:612/792;background:var(--sd-pdf-page-bg);border-radius:2px;outline:1px solid rgba(255,255,255,.08);outline-offset:-1px;box-shadow:0 2px 6px #0000004d;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-user-select:none;user-select:none;background-image:linear-gradient(90deg,#0000,#0000000a,#0000);background-size:200% 100%;animation:sd-preview-pdf-thumb-skel 1.6s ease-in-out infinite}.sd-preview-pdf-thumb__canvas{display:block;max-width:100%;max-height:100%;background:var(--sd-pdf-page-bg)}.sd-preview-pdf-thumb__label{font-size:11px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:-2px}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__label{color:var(--sd-pdf-primary);font-weight:600}.sd-preview-pdf-thumb:focus-visible .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-stage{position:relative;flex:1 1 auto;min-width:0;min-height:0;background:var(--sd-pdf-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.015) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;overflow:auto;padding:32px 24px 96px;display:flex;justify-content:center;align-items:flex-start}:host([data-theme=light]) .sd-preview-pdf-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-pdf-page-wrap{display:inline-block;position:relative}.sd-preview-pdf-page{display:block;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow);max-width:100%}.sd-preview-pdf-continuous-content{width:100%;min-height:100%}.sd-preview-pdf-continuous-spacer{width:1px}.sd-preview-pdf-continuous-slot{display:flex;justify-content:center;align-items:flex-start}.sd-preview-pdf-continuous-slot+.sd-preview-pdf-continuous-slot{margin-top:24px}.sd-preview-pdf-continuous-page{display:block;max-width:100%;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow)}.sd-preview-pdf-toolbar{position:absolute;bottom:20px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:2px;padding:5px 6px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-pdf-divider);border-radius:10px;box-shadow:0 8px 30px #0006;z-index:4;max-width:calc(100% - 32px);overflow-x:auto}.sd-preview-pdf-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:30px;height:28px;padding:0 6px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-toolbar__btn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-toolbar__btn span{font-size:13px;font-weight:500}.sd-preview-pdf-toolbar__btn--with-label{padding:0 10px}.sd-preview-pdf-toolbar__btn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__btn:hover:not(:disabled):not(.sd-preview-pdf-toolbar__btn--active){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-toolbar__btn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-toolbar__btn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-toolbar__page{display:inline-flex;align-items:center;gap:4px;padding:0 6px;flex-shrink:0}.sd-preview-pdf-toolbar__page-input{width:36px;height:24px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:4px;text-align:center;font-size:13px;font-variant-numeric:tabular-nums;outline:none}.sd-preview-pdf-toolbar__page-input:focus{border-color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__page-total{font-size:13px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-toolbar__zoom{min-width:56px;text-align:center;color:var(--sd-pdf-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums;background:#ffffff14;border-radius:4px;padding:2px 8px}.sd-preview-pdf-toolbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);margin:0 4px;flex-shrink:0}.sd-preview-pdf-status{margin:auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:32px;max-width:420px}.sd-preview-pdf-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-status__tile sd-icon{font-size:48px;width:48px;height:48px}.sd-preview-pdf-status__tile--muted{background:#ffffff0d}.sd-preview-pdf-status__tile--muted sd-icon{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-status__tile--danger{background:var(--sd-pdf-danger-soft);border-color:var(--sd-pdf-danger-border)}.sd-preview-pdf-status__tile--danger sd-icon{color:var(--sd-pdf-danger)}.sd-preview-pdf-status__title{font-size:16px;font-weight:500;color:var(--sd-pdf-fg)}.sd-preview-pdf-status__hint{font-size:13px;color:var(--sd-pdf-fg-muted);line-height:1.5}.sd-preview-pdf-status__actions{display:flex;gap:8px;margin-top:4px}.sd-preview-pdf-progress-pct{font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted)}.sd-preview-pdf-progress{width:240px;height:4px;background:#ffffff14;border-radius:999px;overflow:hidden}.sd-preview-pdf-progress__fill{height:100%;background:var(--sd-pdf-primary);transition:width .12s linear}.sd-preview-pdf-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-pdf-primary);animation:sd-preview-pdf-spin .9s linear infinite}@keyframes sd-preview-pdf-spin{to{transform:rotate(360deg)}}@keyframes sd-preview-pdf-thumb-skel{0%{background-position:200% 0}to{background-position:-200% 0}}.sd-preview-pdf-search-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.sd-preview-pdf-search-result{width:100%;display:flex;align-items:flex-start;gap:8px;padding:8px 10px;background:#ffffff0a;border:1px solid transparent;border-radius:6px;color:var(--sd-pdf-fg);cursor:pointer;text-align:left;transition:background .12s,border-color .12s}.sd-preview-pdf-search-result:hover{background:#ffffff12}.sd-preview-pdf-search-result--active{background:var(--sd-pdf-primary-soft);border-color:#005cbb73}.sd-preview-pdf-search-result__snippet{flex:1 1 auto;font-size:12px;line-height:1.4;color:var(--sd-pdf-fg);word-break:break-word}.sd-preview-pdf-search-result__snippet mark{background:var(--sd-pdf-search-hi);color:#000;padding:0 2px;border-radius:2px}.sd-preview-pdf-search-result__page{flex:0 0 auto;font-size:11px;font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted);background:#ffffff0f;padding:2px 6px;border-radius:4px}.sd-preview-pdf-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-pdf-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-pdf-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-primary:hover{filter:brightness(1.1)}.sd-preview-pdf-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-pdf-btn-secondary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:transparent;color:var(--sd-pdf-fg);border:1px solid var(--sd-pdf-divider);border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:background .12s}.sd-preview-pdf-btn-secondary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-secondary:hover{background:#ffffff0f}.sd-preview-pdf-btn-secondary:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}\n"] }]
3238
+ }, template: "<!--\n sd-preview-pdf shell. Self-contained \u2014 sizes to its parent. Consumer wraps\n in <sd-modal>/<sd-side-drawer>/page section. Header + sidebar + stage +\n floating toolbar mirror Artboards A / C / E from the design handoff.\n-->\n<div class=\"sd-preview-pdf-shell\" role=\"region\">\n @let _stage = stage();\n @let _numPages = numPages();\n @let _activePage = activePage();\n @let _isSidebarOpen = isSidebarOpen();\n @let _searchBarOpen = searchBarOpen();\n @let _searchTerm = searchTerm();\n @let _searchTotal = searchTotal();\n\n <!-- ============================================\n HEADER BAR \u2014 hidden in empty + loading per design\n ============================================ -->\n @if (_stage === 'ready') {\n <div class=\"sd-preview-pdf-header\">\n <div class=\"sd-preview-pdf-header__left\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_isSidebarOpen\"\n [attr.aria-expanded]=\"_isSidebarOpen\"\n [attr.aria-controls]=\"sidebarRegionId()\"\n [attr.aria-label]=\"'core.component.preview-pdf.toggle-sidebar' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.toggle-sidebar' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSidebarToggle()\"\n (click)=\"toggleSidebar()\">\n <sd-icon fontIcon=\"menu\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-header__divider\"></span>\n <div class=\"sd-preview-pdf-fileicon\" aria-hidden=\"true\">PDF</div>\n <div class=\"sd-preview-pdf-header__meta\">\n <div class=\"sd-preview-pdf-header__name\" [title]=\"title() || filename()\">\n {{ title() || filename() }}\n </div>\n <div class=\"sd-preview-pdf-header__sub\">\n {{ 'core.component.preview-pdf.pages-count' | sdTranslate: { count: _numPages } }}\n </div>\n </div>\n </div>\n\n <div class=\"sd-preview-pdf-header__right\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"_searchBarOpen\"\n [attr.aria-expanded]=\"_searchBarOpen\"\n [attr.aria-controls]=\"searchBarId()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchToggle()\"\n (click)=\"searchBarOpen() ? closeSearch() : openSearch()\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n </button>\n @if (canPrint()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.print' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.print' | sdTranslate\"\n [attr.data-autoId]=\"autoIdPrint()\"\n (click)=\"printFile()\">\n <sd-icon fontIcon=\"print\"></sd-icon>\n </button>\n }\n @if (canDownload()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.download' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.download' | sdTranslate\"\n [attr.data-autoId]=\"autoIdDownload()\"\n (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n </button>\n }\n @if (canFullscreen()) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.fullscreen' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.fullscreen' | sdTranslate\"\n [attr.data-autoId]=\"autoIdFullscreen()\"\n (click)=\"toggleFullscreen()\">\n <sd-icon [fontIcon]=\"isFullscreen() ? 'fullscreen_exit' : 'fullscreen'\"></sd-icon>\n </button>\n }\n </div>\n </div>\n\n <!-- ============================================\n SEARCH BAR \u2014 between header + body, only when bar is open\n ============================================ -->\n <div\n class=\"sd-preview-pdf-searchbar\"\n role=\"search\"\n [id]=\"searchBarId()\"\n [hidden]=\"!_searchBarOpen\"\n [style.display]=\"_searchBarOpen ? null : 'none'\">\n @if (_searchBarOpen) {\n <sd-icon class=\"sd-preview-pdf-searchbar__icon\" fontIcon=\"search\"></sd-icon>\n <input\n #searchInput\n type=\"text\"\n class=\"sd-preview-pdf-searchbar__input\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-placeholder' | sdTranslate\"\n [attr.placeholder]=\"'core.component.preview-pdf.search-placeholder' | sdTranslate\"\n [value]=\"_searchTerm\"\n [attr.data-autoId]=\"autoIdSearchInput()\"\n (input)=\"onSearchInput($event)\" />\n <span class=\"sd-preview-pdf-searchbar__counter\" aria-live=\"polite\">\n {{ searchCurrent() }} / {{ _searchTotal }}{{ searchTruncated() ? '+' : '' }}\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-prev' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search-prev' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchPrev()\"\n (click)=\"searchPrev()\">\n <sd-icon fontIcon=\"keyboard_arrow_up\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [disabled]=\"_searchTotal === 0\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-next' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search-next' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchNext()\"\n (click)=\"searchNext()\">\n <sd-icon fontIcon=\"keyboard_arrow_down\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__divider\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchCaseSensitive()\"\n [attr.aria-pressed]=\"searchCaseSensitive()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-case' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search-case' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchCase()\"\n (click)=\"toggleSearchCaseSensitive()\">\n <sd-icon fontIcon=\"match_case\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [class.sd-preview-pdf-iconbtn--active]=\"searchWholeWord()\"\n [attr.aria-pressed]=\"searchWholeWord()\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-whole' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search-whole' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchWhole()\"\n (click)=\"toggleSearchWholeWord()\">\n <sd-icon fontIcon=\"text_fields\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-searchbar__spacer\"></span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-iconbtn\"\n [attr.aria-label]=\"'core.component.preview-pdf.search-close' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.search-close' | sdTranslate\"\n [attr.data-autoId]=\"autoIdSearchClose()\"\n (click)=\"closeSearch()\">\n <sd-icon fontIcon=\"close\"></sd-icon>\n </button>\n }\n </div>\n }\n\n <!-- ============================================\n BODY \u2014 sidebar + stage\n ============================================ -->\n <div class=\"sd-preview-pdf-body\">\n <!-- SIDEBAR ............................................................. -->\n @if (_stage === 'ready') {\n <aside\n class=\"sd-preview-pdf-sidebar\"\n role=\"complementary\"\n [id]=\"sidebarRegionId()\"\n [hidden]=\"!_isSidebarOpen\"\n [style.display]=\"_isSidebarOpen ? null : 'none'\">\n @if (_isSidebarOpen) {\n @let _sidebarMode = sidebarMode();\n <div class=\"sd-preview-pdf-sidebar__tabs\" role=\"tablist\">\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabThumbnailsId()\"\n [attr.aria-selected]=\"_sidebarMode === 'thumbnails'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'thumbnails' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'thumbnails'\"\n data-sidebar-mode=\"thumbnails\"\n [attr.data-autoId]=\"autoIdTabThumbnails()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'thumbnails')\"\n (click)=\"setSidebarMode('thumbnails')\">\n <sd-icon fontIcon=\"view_carousel\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.thumbnails' | sdTranslate }}</span>\n </button>\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabOutlineId()\"\n [attr.aria-selected]=\"_sidebarMode === 'outline'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'outline' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'outline'\"\n data-sidebar-mode=\"outline\"\n [attr.data-autoId]=\"autoIdTabOutline()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'outline')\"\n (click)=\"setSidebarMode('outline')\">\n <sd-icon fontIcon=\"list\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.outline' | sdTranslate }}</span>\n </button>\n <button\n #sidebarTab\n type=\"button\"\n class=\"sd-preview-pdf-sidebar__tab\"\n role=\"tab\"\n [id]=\"tabSearchId()\"\n [attr.aria-selected]=\"_sidebarMode === 'search'\"\n [attr.aria-controls]=\"sidebarPanelId()\"\n [attr.tabindex]=\"_sidebarMode === 'search' ? 0 : -1\"\n [class.sd-preview-pdf-sidebar__tab--active]=\"_sidebarMode === 'search'\"\n data-sidebar-mode=\"search\"\n [attr.data-autoId]=\"autoIdTabSearch()\"\n (keydown)=\"onSidebarTabKeyDown($event, 'search')\"\n (click)=\"setSidebarMode('search')\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.tab.search' | sdTranslate }}</span>\n </button>\n </div>\n\n <div\n #sidebarContent\n class=\"sd-preview-pdf-sidebar__content\"\n role=\"tabpanel\"\n [id]=\"sidebarPanelId()\"\n [attr.aria-labelledby]=\"sidebarActiveTabId()\"\n (scroll)=\"onThumbnailSidebarScroll($event)\">\n @switch (_sidebarMode) {\n @case ('thumbnails') {\n <ul class=\"sd-preview-pdf-thumb-list\">\n <li class=\"sd-preview-pdf-thumb-list__spacer\" aria-hidden=\"true\" [style.height.px]=\"thumbnailTopSpacer()\"></li>\n @for (p of pageNumbers(); track trackByPage($index, p)) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-thumb\"\n [class.sd-preview-pdf-thumb--active]=\"p === _activePage\"\n [attr.aria-current]=\"p === _activePage ? 'page' : null\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | sdTranslate: { current: p, total: _numPages }\"\n [attr.data-autoId]=\"autoIdThumb(p - 1)\"\n (click)=\"goToPage(p)\">\n <div class=\"sd-preview-pdf-thumb__page\">\n <canvas #thumbCanvas class=\"sd-preview-pdf-thumb__canvas\" [attr.data-page]=\"p\" aria-hidden=\"true\"></canvas>\n </div>\n <span class=\"sd-preview-pdf-thumb__label\">{{ p }}</span>\n </button>\n </li>\n }\n <li class=\"sd-preview-pdf-thumb-list__spacer\" aria-hidden=\"true\" [style.height.px]=\"thumbnailBottomSpacer()\"></li>\n </ul>\n }\n @case ('outline') {\n <ul\n class=\"sd-preview-pdf-outline-tree\"\n role=\"tree\"\n [attr.aria-label]=\"'core.component.preview-pdf.tab.outline' | sdTranslate\">\n @for (row of visibleOutline(); track row.item.id) {\n <li class=\"sd-preview-pdf-outline-tree__node\" role=\"none\">\n <div class=\"sd-preview-pdf-outline-tree__row\" [style.padding-left.px]=\"(row.level - 1) * 16\">\n @if (row.item.children.length > 0) {\n <button\n type=\"button\"\n class=\"sd-preview-pdf-outline-tree__toggle\"\n tabindex=\"-1\"\n [attr.aria-label]=\"row.item.title\"\n [attr.aria-expanded]=\"isOutlineExpanded(row.item.id)\"\n (click)=\"toggleOutlineItem(row.item.id, $event)\">\n <sd-icon [fontIcon]=\"isOutlineExpanded(row.item.id) ? 'expand_more' : 'chevron_right'\"></sd-icon>\n </button>\n } @else {\n <span class=\"sd-preview-pdf-outline-tree__toggle-placeholder\" aria-hidden=\"true\"></span>\n }\n\n <!-- why: role=\"treeitem\" B\u1EAET BU\u1ED8C c\u00F3 aria-selected \u2014 thi\u1EBFu n\u00F3 screen reader\n kh\u00F4ng \u0111\u1ECDc \u0111\u01B0\u1EE3c m\u1EE5c outline n\u00E0o \u0111ang l\u00E0 m\u1EE5c hi\u1EC7n h\u00E0nh. \u1EDE \u0111\u00E2y \"\u0111\u01B0\u1EE3c ch\u1ECDn\"\n = m\u1EE5c tr\u1ECF t\u1EDBi trang \u0111ang xem, \u0111\u00FAng b\u1EB1ng \u0111i\u1EC1u ki\u1EC7n c\u1EE7a aria-current. -->\n @if (row.item.url && row.item.page === null) {\n <a\n #outlineItem\n class=\"sd-preview-pdf-outline-tree__item\"\n role=\"treeitem\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n [href]=\"row.item.url\"\n [attr.data-outline-id]=\"row.item.id\"\n [attr.aria-level]=\"row.level\"\n [attr.aria-expanded]=\"row.item.children.length > 0 ? isOutlineExpanded(row.item.id) : null\"\n [attr.aria-selected]=\"row.item.page === _activePage\"\n [attr.aria-current]=\"row.item.page === _activePage ? 'page' : null\"\n [attr.tabindex]=\"outlineFocusId() === row.item.id ? 0 : -1\"\n (focus)=\"focusOutlineItem(row.item.id)\"\n (keydown)=\"onOutlineKeyDown($event, row.item.id)\">\n {{ row.item.title }}\n </a>\n } @else {\n <button\n #outlineItem\n type=\"button\"\n class=\"sd-preview-pdf-outline-tree__item\"\n role=\"treeitem\"\n [attr.data-outline-id]=\"row.item.id\"\n [attr.aria-level]=\"row.level\"\n [attr.aria-expanded]=\"row.item.children.length > 0 ? isOutlineExpanded(row.item.id) : null\"\n [attr.aria-selected]=\"row.item.page === _activePage\"\n [attr.aria-current]=\"row.item.page === _activePage ? 'page' : null\"\n [attr.tabindex]=\"outlineFocusId() === row.item.id ? 0 : -1\"\n (focus)=\"focusOutlineItem(row.item.id)\"\n (click)=\"activateOutlineItem(row.item.id)\"\n (keydown)=\"onOutlineKeyDown($event, row.item.id)\">\n {{ row.item.title }}\n </button>\n }\n </div>\n </li>\n }\n </ul>\n }\n @case ('search') {\n @if (!_searchTerm) {\n <div class=\"sd-preview-pdf-sidebar__message\">\n <sd-icon fontIcon=\"search\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-placeholder' | sdTranslate }}</p>\n </div>\n } @else if (_searchTotal === 0) {\n <div class=\"sd-preview-pdf-sidebar__message\">\n <sd-icon fontIcon=\"search_off\"></sd-icon>\n <p>{{ 'core.component.preview-pdf.search-no-results' | sdTranslate }}</p>\n </div>\n } @else {\n @if (searchTruncated()) {\n <p class=\"sd-preview-pdf-search-limit\" role=\"status\">\n {{ 'core.component.preview-pdf.search-truncated' | sdTranslate: { count: _searchTotal } }}\n </p>\n }\n <ul class=\"sd-preview-pdf-search-list\">\n @for (r of searchResults(); track $index; let i = $index) {\n <li>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-search-result\"\n [class.sd-preview-pdf-search-result--active]=\"i === searchActiveIndex()\"\n [attr.data-autoId]=\"autoIdResult(i)\"\n (click)=\"activateSearchResult(i)\">\n <span class=\"sd-preview-pdf-search-result__snippet\">\n {{ r.before }}<mark>{{ r.term }}</mark\n >{{ r.after }}\n </span>\n <span class=\"sd-preview-pdf-search-result__page\">{{ r.page }}</span>\n </button>\n </li>\n }\n </ul>\n }\n }\n }\n </div>\n }\n </aside>\n }\n\n <!-- STAGE ............................................................... -->\n <div class=\"sd-preview-pdf-stage\" #stageRef (wheel)=\"onWheel($event)\" (scroll)=\"onStageScroll()\">\n @switch (_stage) {\n @case ('empty') {\n <div class=\"sd-preview-pdf-status\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--muted\">\n <sd-icon fontIcon=\"picture_as_pdf\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.empty.title' | sdTranslate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.empty.subtitle' | sdTranslate }}\n </div>\n </div>\n }\n @case ('loading') {\n @let _loadPercent = loadPercent();\n <div class=\"sd-preview-pdf-status\">\n <div\n class=\"sd-preview-pdf-spinner\"\n role=\"progressbar\"\n [attr.aria-label]=\"'core.component.preview-pdf.loading' | sdTranslate\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n [attr.aria-valuenow]=\"_loadPercent\"></div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.loading' | sdTranslate }}\n @if (_loadPercent > 0) {\n <span class=\"sd-preview-pdf-progress-pct\">&nbsp;{{ _loadPercent }}%</span>\n }\n </div>\n <div class=\"sd-preview-pdf-progress\">\n <div class=\"sd-preview-pdf-progress__fill\" [style.width.%]=\"_loadPercent\"></div>\n </div>\n </div>\n }\n @case ('error') {\n @if (loadError$(); as err) {\n <div class=\"sd-preview-pdf-status\" role=\"alert\" aria-live=\"assertive\">\n <div class=\"sd-preview-pdf-status__tile sd-preview-pdf-status__tile--danger\">\n <sd-icon fontIcon=\"error\"></sd-icon>\n </div>\n <div class=\"sd-preview-pdf-status__title\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.title' | sdTranslate }}\n </div>\n <div class=\"sd-preview-pdf-status__hint\">\n {{ 'core.component.preview-pdf.error.' + err.reason + '.body' | sdTranslate }}\n </div>\n <div class=\"sd-preview-pdf-status__actions\">\n <button type=\"button\" class=\"sd-preview-pdf-btn-primary\" (click)=\"retryLoad()\" [attr.data-autoId]=\"autoIdRetry()\">\n <sd-icon fontIcon=\"refresh\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.retry' | sdTranslate }}</span>\n </button>\n @if (canDownload()) {\n <button type=\"button\" class=\"sd-preview-pdf-btn-secondary\" (click)=\"downloadFile()\">\n <sd-icon fontIcon=\"download\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.error.download-raw' | sdTranslate }}</span>\n </button>\n }\n </div>\n </div>\n }\n }\n @default {\n @if (scrollModeCurrent() === 'continuous') {\n <div class=\"sd-preview-pdf-continuous-content\">\n <div class=\"sd-preview-pdf-continuous-spacer\" [style.height.px]=\"continuousTopSpacer()\"></div>\n @for (pageNumber of continuousPages(); track pageNumber) {\n <div class=\"sd-preview-pdf-continuous-slot\" [style.min-height.px]=\"continuousPageHeights()[pageNumber]\">\n <canvas\n #continuousCanvas\n class=\"sd-preview-pdf-continuous-page\"\n role=\"img\"\n [attr.data-page]=\"pageNumber\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | sdTranslate: { current: pageNumber, total: _numPages }\">\n </canvas>\n </div>\n }\n <div class=\"sd-preview-pdf-continuous-spacer\" [style.height.px]=\"continuousBottomSpacer()\"></div>\n </div>\n } @else {\n <div class=\"sd-preview-pdf-page-wrap\">\n <canvas\n #pageCanvas\n class=\"sd-preview-pdf-page\"\n role=\"img\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | sdTranslate: { current: _activePage, total: _numPages }\">\n </canvas>\n </div>\n }\n }\n }\n\n <!-- FLOATING TOOLBAR ........................................ -->\n @if (showToolbar() && _stage === 'ready') {\n @let _canPrev = canPrev();\n @let _canNext = canNext();\n @let _zoomMode = zoomMode();\n @let _scrollModeCurrent = scrollModeCurrent();\n <div class=\"sd-preview-pdf-toolbar\" role=\"toolbar\">\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.first-page' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.first-page' | sdTranslate\"\n [attr.data-autoId]=\"autoIdFirst()\"\n (click)=\"firstPage()\">\n <sd-icon fontIcon=\"first_page\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canPrev\"\n [attr.aria-label]=\"'core.component.preview-pdf.prev-page' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.prev-page' | sdTranslate\"\n [attr.data-autoId]=\"autoIdPrev()\"\n (click)=\"prevPage()\">\n <sd-icon fontIcon=\"chevron_left\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__page\">\n <input\n type=\"text\"\n inputmode=\"numeric\"\n class=\"sd-preview-pdf-toolbar__page-input\"\n [value]=\"_activePage\"\n #pageInput\n (keydown.enter)=\"onPageInputEnter($event)\"\n (blur)=\"onPageInputBlur($event)\"\n [attr.aria-label]=\"'core.component.preview-pdf.page-of' | sdTranslate: { current: _activePage, total: _numPages }\"\n [attr.data-autoId]=\"autoIdPageInput()\" />\n <span class=\"sd-preview-pdf-toolbar__page-total\">/ {{ _numPages }}</span>\n </span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.next-page' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.next-page' | sdTranslate\"\n [attr.data-autoId]=\"autoIdNext()\"\n (click)=\"nextPage()\">\n <sd-icon fontIcon=\"chevron_right\"></sd-icon>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [disabled]=\"!_canNext\"\n [attr.aria-label]=\"'core.component.preview-pdf.last-page' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.last-page' | sdTranslate\"\n [attr.data-autoId]=\"autoIdLast()\"\n (click)=\"lastPage()\">\n <sd-icon fontIcon=\"last_page\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-out' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-out' | sdTranslate\"\n [attr.data-autoId]=\"autoIdZoomOut()\"\n (click)=\"zoomOut()\">\n <sd-icon fontIcon=\"remove\"></sd-icon>\n </button>\n <span class=\"sd-preview-pdf-toolbar__zoom\" aria-live=\"polite\" [attr.data-autoId]=\"autoIdZoomReadout()\">{{ zoomPercent() }}%</span>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.zoom-in' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.zoom-in' | sdTranslate\"\n [attr.data-autoId]=\"autoIdZoomIn()\"\n (click)=\"zoomIn()\">\n <sd-icon fontIcon=\"add\"></sd-icon>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-fit'\"\n [attr.data-autoId]=\"autoIdFitPage()\"\n (click)=\"setZoom('page-fit')\">\n <sd-icon fontIcon=\"fit_screen\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-page' | sdTranslate }}</span>\n </button>\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [class.sd-preview-pdf-toolbar__btn--active]=\"_zoomMode === 'page-width'\"\n [attr.data-autoId]=\"autoIdFitWidth()\"\n (click)=\"setZoom('page-width')\">\n <sd-icon fontIcon=\"swap_horiz\"></sd-icon>\n <span>{{ 'core.component.preview-pdf.fit-width' | sdTranslate }}</span>\n </button>\n\n <span class=\"sd-preview-pdf-toolbar__divider\"></span>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn\"\n [attr.aria-label]=\"'core.component.preview-pdf.rotate' | sdTranslate\"\n [attr.title]=\"'core.component.preview-pdf.rotate' | sdTranslate\"\n [attr.data-autoId]=\"autoIdRotate()\"\n (click)=\"rotate('right')\">\n <sd-icon fontIcon=\"rotate_right\"></sd-icon>\n </button>\n\n <button\n type=\"button\"\n class=\"sd-preview-pdf-toolbar__btn sd-preview-pdf-toolbar__btn--with-label\"\n [attr.data-autoId]=\"autoIdScrollMode()\"\n (click)=\"setScrollMode(_scrollModeCurrent === 'page' ? 'continuous' : 'page')\">\n <sd-icon [fontIcon]=\"_scrollModeCurrent === 'page' ? 'description' : 'view_day'\"></sd-icon>\n <span>\n {{\n (_scrollModeCurrent === 'page' ? 'core.component.preview-pdf.scroll-page' : 'core.component.preview-pdf.scroll-continuous')\n | sdTranslate\n }}\n </span>\n </button>\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{--sd-pdf-shell-bg: #1a1c1f;--sd-pdf-sidebar-bg: #22252a;--sd-pdf-toolbar-bg: rgba(20, 22, 25, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .4);--sd-pdf-divider: rgba(255, 255, 255, .08);--sd-pdf-fg: #f5f6f7;--sd-pdf-fg-muted: rgba(255, 255, 255, .6);--sd-pdf-fg-subtle: rgba(255, 255, 255, .4);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .18));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .1);--sd-pdf-danger-border: rgba(186, 26, 26, .3);--sd-pdf-danger-tint: rgba(186, 26, 26, .15);--sd-pdf-hover-bg: rgba(255, 255, 255, .1);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(249, 115, 22, .85);display:block;width:100%;height:100%;min-height:320px;outline:none}:host:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}:host([data-theme=light]){--sd-pdf-shell-bg: #f7f8fa;--sd-pdf-sidebar-bg: #ffffff;--sd-pdf-toolbar-bg: rgba(255, 255, 255, .94);--sd-pdf-page-bg: #ffffff;--sd-pdf-page-shadow: 0 4px 16px rgba(0, 0, 0, .12);--sd-pdf-divider: rgba(0, 0, 0, .08);--sd-pdf-fg: #1a1c1f;--sd-pdf-fg-muted: rgba(0, 0, 0, .62);--sd-pdf-fg-subtle: rgba(0, 0, 0, .42);--sd-pdf-primary: var(--sd-pdf-accent, var(--sd-primary, #005cbb));--sd-pdf-primary-soft: var(--sd-pdf-accent-soft, rgba(0, 92, 187, .16));--sd-pdf-danger: var(--sd-error, #ba1a1a);--sd-pdf-success: var(--sd-success, #2e7d32);--sd-pdf-danger-soft: rgba(186, 26, 26, .08);--sd-pdf-danger-border: rgba(186, 26, 26, .25);--sd-pdf-danger-tint: rgba(186, 26, 26, .12);--sd-pdf-hover-bg: rgba(0, 0, 0, .06);--sd-pdf-search-hi: rgba(250, 204, 21, .55);--sd-pdf-search-hi-active: rgba(250, 204, 21, .9)}:host(.sd-preview-pdf-host--fullscreen){width:100vw;height:100vh;min-height:0}.sd-preview-pdf-shell{position:relative;display:flex;flex-direction:column;width:100%;height:100%;background:var(--sd-pdf-shell-bg);color:var(--sd-pdf-fg);border-radius:12px;overflow:hidden;box-shadow:0 30px 80px #0000008c,0 0 0 1px #ffffff0a;font-family:inherit}.sd-preview-pdf-header{display:flex;align-items:center;justify-content:space-between;gap:12px;height:57px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-bottom:1px solid var(--sd-pdf-divider);flex:0 0 auto;z-index:2}.sd-preview-pdf-header__left{display:flex;align-items:center;gap:8px;min-width:0;flex:1 1 auto}.sd-preview-pdf-header__right{display:flex;align-items:center;gap:4px;flex-shrink:0}.sd-preview-pdf-header__divider{display:inline-block;width:1px;height:20px;background:var(--sd-pdf-divider)}.sd-preview-pdf-header__meta{display:flex;flex-direction:column;min-width:0}.sd-preview-pdf-header__name{font-size:14px;font-weight:500;color:var(--sd-pdf-fg);max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.sd-preview-pdf-header__sub{font-size:11px;color:var(--sd-pdf-fg-muted);margin-top:2px}.sd-preview-pdf-fileicon{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:6px;background:var(--sd-pdf-danger-tint);border:1px solid var(--sd-pdf-danger-border);color:var(--sd-error-light, #f87171);font:700 10px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.sd-preview-pdf-iconbtn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-width:32px;height:32px;padding:0 8px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;transition:background .12s,color .12s;white-space:nowrap;flex-shrink:0}.sd-preview-pdf-iconbtn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-iconbtn__label{font-size:13px;font-weight:500}.sd-preview-pdf-iconbtn:hover:not(:disabled){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-iconbtn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-iconbtn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-iconbtn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-searchbar{display:flex;align-items:center;gap:6px;height:46px;padding:0 12px;background:var(--sd-pdf-toolbar-bg);border-bottom:1px solid var(--sd-pdf-divider);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);flex:0 0 auto;z-index:2}.sd-preview-pdf-searchbar__icon{color:var(--sd-pdf-fg-muted);font-size:18px;width:18px;height:18px;flex-shrink:0}.sd-preview-pdf-searchbar__input{flex:0 1 360px;min-width:160px;height:30px;background:#ffffff0f;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:6px;padding:0 10px;font-size:13px;outline:none}.sd-preview-pdf-searchbar__input::placeholder{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-searchbar__input:focus{border-color:var(--sd-pdf-primary);background:#ffffff17}.sd-preview-pdf-searchbar__counter{color:var(--sd-pdf-fg-muted);font-size:12px;font-variant-numeric:tabular-nums;min-width:48px;text-align:center;flex-shrink:0}.sd-preview-pdf-searchbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);flex-shrink:0}.sd-preview-pdf-searchbar__spacer{flex:1 1 auto}.sd-pdf-search-overlay{position:absolute;top:8px;left:50%;transform:translate(-50%);display:flex;flex-wrap:wrap;gap:4px;max-width:80%;pointer-events:none;z-index:5}.sd-pdf-search-hi{display:inline-block;padding:1px 4px;border-radius:2px;background:var(--sd-pdf-search-hi);color:#000;font-size:12px;font-weight:500}.sd-pdf-search-hi--active{background:var(--sd-pdf-search-hi-active);color:#fff;box-shadow:0 0 0 2px #fff6}.sd-preview-pdf-body{position:relative;flex:1 1 auto;min-height:0;display:flex;flex-direction:row;overflow:hidden}.sd-preview-pdf-sidebar{width:240px;display:flex;flex-direction:column;background:var(--sd-pdf-sidebar-bg);border-right:1px solid var(--sd-pdf-divider);flex-shrink:0;min-height:0}.sd-preview-pdf-sidebar__tabs{display:flex;gap:4px;padding:8px;border-bottom:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-sidebar__tab{flex:1 1 0;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;padding:0 4px;background:transparent;border:none;color:var(--sd-pdf-fg-muted);border-radius:6px;cursor:pointer;font-size:12px;font-weight:500;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-sidebar__tab sd-icon{font-size:14px;width:14px;height:14px}.sd-preview-pdf-sidebar__tab:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-sidebar__tab--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-sidebar__content{flex:1 1 auto;overflow:auto;padding:12px}.sd-preview-pdf-sidebar__message{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:32px 16px;text-align:center;color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-sidebar__message sd-icon{font-size:28px;width:28px;height:28px;opacity:.6}.sd-preview-pdf-sidebar__message p{margin:0;font-size:12px;line-height:1.5}.sd-preview-pdf-outline-tree{list-style:none;margin:0;padding:0}.sd-preview-pdf-outline-tree__node{margin:0;padding:0}.sd-preview-pdf-outline-tree__row{display:flex;align-items:center;min-width:0}.sd-preview-pdf-outline-tree__toggle,.sd-preview-pdf-outline-tree__toggle-placeholder{width:24px;height:28px;flex:0 0 24px}.sd-preview-pdf-outline-tree__toggle{display:inline-flex;align-items:center;justify-content:center;padding:0;border:0;border-radius:4px;background:transparent;color:var(--sd-pdf-fg-muted);cursor:pointer}.sd-preview-pdf-outline-tree__toggle:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-outline-tree__toggle sd-icon{width:16px;height:16px;font-size:16px}.sd-preview-pdf-outline-tree__item{min-width:0;flex:1 1 auto;overflow:hidden;padding:6px 8px;border:0;border-radius:5px;background:transparent;color:var(--sd-pdf-fg-muted);font:inherit;font-size:12px;line-height:1.35;text-align:left;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;cursor:pointer}.sd-preview-pdf-outline-tree__item:hover{background:#ffffff0f;color:var(--sd-pdf-fg)}.sd-preview-pdf-outline-tree__item[aria-current=page]{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary);font-weight:600}.sd-preview-pdf-outline-tree__item:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:-2px}.sd-preview-pdf-thumb-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:12px}.sd-preview-pdf-thumb-list__spacer{flex:0 0 auto;pointer-events:none}.sd-preview-pdf-search-limit{margin:0 0 8px;color:var(--sd-pdf-fg-muted);font-size:11px;line-height:1.4}.sd-preview-pdf-thumb{display:flex;flex-direction:column;align-items:center;gap:4px;width:100%;padding:0;background:transparent;border:none;cursor:pointer}.sd-preview-pdf-thumb__page{width:152px;aspect-ratio:612/792;background:var(--sd-pdf-page-bg);border-radius:2px;outline:1px solid rgba(255,255,255,.08);outline-offset:-1px;box-shadow:0 2px 6px #0000004d;display:flex;align-items:center;justify-content:center;overflow:hidden;-webkit-user-select:none;user-select:none;background-image:linear-gradient(90deg,#0000,#0000000a,#0000);background-size:200% 100%;animation:sd-preview-pdf-thumb-skel 1.6s ease-in-out infinite}.sd-preview-pdf-thumb__canvas{display:block;max-width:100%;max-height:100%;background:var(--sd-pdf-page-bg)}.sd-preview-pdf-thumb__label{font-size:11px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:-2px}.sd-preview-pdf-thumb--active .sd-preview-pdf-thumb__label{color:var(--sd-pdf-primary);font-weight:600}.sd-preview-pdf-thumb:focus-visible .sd-preview-pdf-thumb__page{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-stage{position:relative;flex:1 1 auto;min-width:0;min-height:0;background:var(--sd-pdf-shell-bg);background-image:linear-gradient(45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.015) 75%);background-size:24px 24px;background-position:0 0,0 12px,12px -12px,-12px 0;overflow:auto;padding:32px 24px 96px;display:flex;justify-content:center;align-items:flex-start}:host([data-theme=light]) .sd-preview-pdf-stage{background-image:linear-gradient(45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.015) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.015) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.015) 75%)}.sd-preview-pdf-page-wrap{display:inline-block;position:relative}.sd-preview-pdf-page{display:block;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow);max-width:100%}.sd-preview-pdf-continuous-content{width:100%;min-height:100%}.sd-preview-pdf-continuous-spacer{width:1px}.sd-preview-pdf-continuous-slot{display:flex;justify-content:center;align-items:flex-start}.sd-preview-pdf-continuous-slot+.sd-preview-pdf-continuous-slot{margin-top:24px}.sd-preview-pdf-continuous-page{display:block;max-width:100%;background:var(--sd-pdf-page-bg);border-radius:2px;box-shadow:var(--sd-pdf-page-shadow)}.sd-preview-pdf-toolbar{position:absolute;bottom:20px;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:2px;padding:5px 6px;background:var(--sd-pdf-toolbar-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);border:1px solid var(--sd-pdf-divider);border-radius:10px;box-shadow:0 8px 30px #0006;z-index:4;max-width:calc(100% - 32px);overflow-x:auto}.sd-preview-pdf-toolbar__btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;min-width:30px;height:28px;padding:0 6px;background:transparent;border:none;color:var(--sd-pdf-fg);border-radius:6px;cursor:pointer;white-space:nowrap;flex-shrink:0;transition:background .12s,color .12s}.sd-preview-pdf-toolbar__btn sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-toolbar__btn span{font-size:13px;font-weight:500}.sd-preview-pdf-toolbar__btn--with-label{padding:0 10px}.sd-preview-pdf-toolbar__btn--active{background:var(--sd-pdf-primary-soft);color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__btn:hover:not(:disabled):not(.sd-preview-pdf-toolbar__btn--active){background:var(--sd-pdf-hover-bg)}.sd-preview-pdf-toolbar__btn:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}.sd-preview-pdf-toolbar__btn:disabled{opacity:.35;cursor:not-allowed}.sd-preview-pdf-toolbar__page{display:inline-flex;align-items:center;gap:4px;padding:0 6px;flex-shrink:0}.sd-preview-pdf-toolbar__page-input{width:36px;height:24px;background:#ffffff14;border:1px solid rgba(255,255,255,.08);color:var(--sd-pdf-fg);border-radius:4px;text-align:center;font-size:13px;font-variant-numeric:tabular-nums;outline:none}.sd-preview-pdf-toolbar__page-input:focus{border-color:var(--sd-pdf-primary)}.sd-preview-pdf-toolbar__page-total{font-size:13px;color:var(--sd-pdf-fg-muted);font-variant-numeric:tabular-nums}.sd-preview-pdf-toolbar__zoom{min-width:56px;text-align:center;color:var(--sd-pdf-fg);font-size:13px;font-weight:500;font-variant-numeric:tabular-nums;background:#ffffff14;border-radius:4px;padding:2px 8px}.sd-preview-pdf-toolbar__divider{width:1px;height:20px;background:var(--sd-pdf-divider);margin:0 4px;flex-shrink:0}.sd-preview-pdf-status{margin:auto;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;padding:32px;max-width:420px}.sd-preview-pdf-status__tile{width:96px;height:96px;border-radius:24px;display:flex;align-items:center;justify-content:center;border:1px solid var(--sd-pdf-divider)}.sd-preview-pdf-status__tile sd-icon{font-size:48px;width:48px;height:48px}.sd-preview-pdf-status__tile--muted{background:#ffffff0d}.sd-preview-pdf-status__tile--muted sd-icon{color:var(--sd-pdf-fg-subtle)}.sd-preview-pdf-status__tile--danger{background:var(--sd-pdf-danger-soft);border-color:var(--sd-pdf-danger-border)}.sd-preview-pdf-status__tile--danger sd-icon{color:var(--sd-pdf-danger)}.sd-preview-pdf-status__title{font-size:16px;font-weight:500;color:var(--sd-pdf-fg)}.sd-preview-pdf-status__hint{font-size:13px;color:var(--sd-pdf-fg-muted);line-height:1.5}.sd-preview-pdf-status__actions{display:flex;gap:8px;margin-top:4px}.sd-preview-pdf-progress-pct{font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted)}.sd-preview-pdf-progress{width:240px;height:4px;background:#ffffff14;border-radius:999px;overflow:hidden}.sd-preview-pdf-progress__fill{height:100%;background:var(--sd-pdf-primary);transition:width .12s linear}.sd-preview-pdf-spinner{width:56px;height:56px;border-radius:999px;border:3px solid rgba(255,255,255,.08);border-top-color:var(--sd-pdf-primary);animation:sd-preview-pdf-spin .9s linear infinite}@keyframes sd-preview-pdf-spin{to{transform:rotate(360deg)}}@keyframes sd-preview-pdf-thumb-skel{0%{background-position:200% 0}to{background-position:-200% 0}}.sd-preview-pdf-search-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.sd-preview-pdf-search-result{width:100%;display:flex;align-items:flex-start;gap:8px;padding:8px 10px;background:#ffffff0a;border:1px solid transparent;border-radius:6px;color:var(--sd-pdf-fg);cursor:pointer;text-align:left;transition:background .12s,border-color .12s}.sd-preview-pdf-search-result:hover{background:#ffffff12}.sd-preview-pdf-search-result--active{background:var(--sd-pdf-primary-soft);border-color:#005cbb73}.sd-preview-pdf-search-result__snippet{flex:1 1 auto;font-size:12px;line-height:1.4;color:var(--sd-pdf-fg);word-break:break-word}.sd-preview-pdf-search-result__snippet mark{background:var(--sd-pdf-search-hi);color:#000;padding:0 2px;border-radius:2px}.sd-preview-pdf-search-result__page{flex:0 0 auto;font-size:11px;font-variant-numeric:tabular-nums;color:var(--sd-pdf-fg-muted);background:#ffffff0f;padding:2px 6px;border-radius:4px}.sd-preview-pdf-btn-primary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:var(--sd-pdf-primary);color:#fff;border:none;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:filter .12s}.sd-preview-pdf-btn-primary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-primary:hover{filter:brightness(1.1)}.sd-preview-pdf-btn-primary:focus-visible{outline:2px solid #fff;outline-offset:2px}.sd-preview-pdf-btn-secondary{display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 16px;background:transparent;color:var(--sd-pdf-fg);border:1px solid var(--sd-pdf-divider);border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;transition:background .12s}.sd-preview-pdf-btn-secondary sd-icon{font-size:18px;width:18px;height:18px}.sd-preview-pdf-btn-secondary:hover{background:#ffffff0f}.sd-preview-pdf-btn-secondary:focus-visible{outline:2px solid var(--sd-pdf-primary);outline-offset:2px}\n"] }]
3229
3239
  }], ctorParameters: () => [], propDecorators: { onKeyDown: [{
3230
3240
  type: HostListener,
3231
3241
  args: ['keydown', ['$event']]