@ng-doc/app 22.0.0-beta.2 → 22.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/README.md +20 -29
  2. package/fesm2022/ng-doc-app-classes-content-anchor-controller.mjs +363 -0
  3. package/fesm2022/ng-doc-app-classes-content-anchor-controller.mjs.map +1 -0
  4. package/fesm2022/ng-doc-app-classes-content-controller.mjs +299 -0
  5. package/fesm2022/ng-doc-app-classes-content-controller.mjs.map +1 -0
  6. package/fesm2022/ng-doc-app-classes-default-search-engine.mjs +51 -35
  7. package/fesm2022/ng-doc-app-classes-default-search-engine.mjs.map +1 -1
  8. package/fesm2022/ng-doc-app-classes-root-page.mjs.map +1 -1
  9. package/fesm2022/ng-doc-app-classes-search-engine.mjs.map +1 -1
  10. package/fesm2022/ng-doc-app-classes.mjs +2 -0
  11. package/fesm2022/ng-doc-app-classes.mjs.map +1 -1
  12. package/fesm2022/ng-doc-app-components-api-list.mjs +173 -69
  13. package/fesm2022/ng-doc-app-components-api-list.mjs.map +1 -1
  14. package/fesm2022/ng-doc-app-components-breadcrumb.mjs +13 -10
  15. package/fesm2022/ng-doc-app-components-breadcrumb.mjs.map +1 -1
  16. package/fesm2022/ng-doc-app-components-code.mjs +152 -25
  17. package/fesm2022/ng-doc-app-components-code.mjs.map +1 -1
  18. package/fesm2022/ng-doc-app-components-command-palette.mjs +498 -0
  19. package/fesm2022/ng-doc-app-components-command-palette.mjs.map +1 -0
  20. package/fesm2022/ng-doc-app-components-copy-button.mjs +38 -21
  21. package/fesm2022/ng-doc-app-components-copy-button.mjs.map +1 -1
  22. package/fesm2022/ng-doc-app-components-demo-displayer.mjs +36 -29
  23. package/fesm2022/ng-doc-app-components-demo-displayer.mjs.map +1 -1
  24. package/fesm2022/ng-doc-app-components-demo-frame.mjs +96 -0
  25. package/fesm2022/ng-doc-app-components-demo-frame.mjs.map +1 -0
  26. package/fesm2022/ng-doc-app-components-demo-pane.mjs +39 -36
  27. package/fesm2022/ng-doc-app-components-demo-pane.mjs.map +1 -1
  28. package/fesm2022/ng-doc-app-components-demo.mjs +175 -44
  29. package/fesm2022/ng-doc-app-components-demo.mjs.map +1 -1
  30. package/fesm2022/ng-doc-app-components-fullscreen-button.mjs +12 -9
  31. package/fesm2022/ng-doc-app-components-fullscreen-button.mjs.map +1 -1
  32. package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs +79 -0
  33. package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs.map +1 -0
  34. package/fesm2022/ng-doc-app-components-heading-anchor.mjs +54 -21
  35. package/fesm2022/ng-doc-app-components-heading-anchor.mjs.map +1 -1
  36. package/fesm2022/ng-doc-app-components-image-viewer.mjs +183 -89
  37. package/fesm2022/ng-doc-app-components-image-viewer.mjs.map +1 -1
  38. package/fesm2022/ng-doc-app-components-kind-icon.mjs +42 -25
  39. package/fesm2022/ng-doc-app-components-kind-icon.mjs.map +1 -1
  40. package/fesm2022/ng-doc-app-components-link.mjs +12 -9
  41. package/fesm2022/ng-doc-app-components-link.mjs.map +1 -1
  42. package/fesm2022/ng-doc-app-components-members.mjs +30 -0
  43. package/fesm2022/ng-doc-app-components-members.mjs.map +1 -0
  44. package/fesm2022/ng-doc-app-components-mermaid-viewer.mjs +61 -20
  45. package/fesm2022/ng-doc-app-components-mermaid-viewer.mjs.map +1 -1
  46. package/fesm2022/ng-doc-app-components-navbar.mjs +39 -49
  47. package/fesm2022/ng-doc-app-components-navbar.mjs.map +1 -1
  48. package/fesm2022/ng-doc-app-components-page-header.mjs +73 -10
  49. package/fesm2022/ng-doc-app-components-page-header.mjs.map +1 -1
  50. package/fesm2022/ng-doc-app-components-page-link.mjs +123 -31
  51. package/fesm2022/ng-doc-app-components-page-link.mjs.map +1 -1
  52. package/fesm2022/ng-doc-app-components-page-navigation.mjs +17 -19
  53. package/fesm2022/ng-doc-app-components-page-navigation.mjs.map +1 -1
  54. package/fesm2022/ng-doc-app-components-page-wrapper.mjs +56 -35
  55. package/fesm2022/ng-doc-app-components-page-wrapper.mjs.map +1 -1
  56. package/fesm2022/ng-doc-app-components-page.mjs +168 -24
  57. package/fesm2022/ng-doc-app-components-page.mjs.map +1 -1
  58. package/fesm2022/ng-doc-app-components-playground.mjs +477 -286
  59. package/fesm2022/ng-doc-app-components-playground.mjs.map +1 -1
  60. package/fesm2022/ng-doc-app-components-root.mjs +48 -31
  61. package/fesm2022/ng-doc-app-components-root.mjs.map +1 -1
  62. package/fesm2022/ng-doc-app-components-search-result.mjs +22 -21
  63. package/fesm2022/ng-doc-app-components-search-result.mjs.map +1 -1
  64. package/fesm2022/ng-doc-app-components-search.mjs +64 -33
  65. package/fesm2022/ng-doc-app-components-search.mjs.map +1 -1
  66. package/fesm2022/ng-doc-app-components-sidebar.mjs +86 -61
  67. package/fesm2022/ng-doc-app-components-sidebar.mjs.map +1 -1
  68. package/fesm2022/ng-doc-app-components-tabs.mjs +35 -11
  69. package/fesm2022/ng-doc-app-components-tabs.mjs.map +1 -1
  70. package/fesm2022/ng-doc-app-components-theme-toggle.mjs +33 -21
  71. package/fesm2022/ng-doc-app-components-theme-toggle.mjs.map +1 -1
  72. package/fesm2022/ng-doc-app-components-toc.mjs +267 -90
  73. package/fesm2022/ng-doc-app-components-toc.mjs.map +1 -1
  74. package/fesm2022/ng-doc-app-components.mjs +4 -1
  75. package/fesm2022/ng-doc-app-components.mjs.map +1 -1
  76. package/fesm2022/ng-doc-app-defaults-default-processors.mjs +2 -1
  77. package/fesm2022/ng-doc-app-defaults-default-processors.mjs.map +1 -1
  78. package/fesm2022/ng-doc-app-demo-app.mjs +176 -0
  79. package/fesm2022/ng-doc-app-demo-app.mjs.map +1 -0
  80. package/fesm2022/ng-doc-app-directives-code-highlighter.mjs +11 -5
  81. package/fesm2022/ng-doc-app-directives-code-highlighter.mjs.map +1 -1
  82. package/fesm2022/ng-doc-app-directives-members-filter.mjs +276 -0
  83. package/fesm2022/ng-doc-app-directives-members-filter.mjs.map +1 -0
  84. package/fesm2022/ng-doc-app-directives-route-active.mjs +34 -30
  85. package/fesm2022/ng-doc-app-directives-route-active.mjs.map +1 -1
  86. package/fesm2022/ng-doc-app-directives.mjs +1 -0
  87. package/fesm2022/ng-doc-app-directives.mjs.map +1 -1
  88. package/fesm2022/ng-doc-app-helpers.mjs +307 -2
  89. package/fesm2022/ng-doc-app-helpers.mjs.map +1 -1
  90. package/fesm2022/ng-doc-app-pipes-decode-uri-component.mjs +6 -4
  91. package/fesm2022/ng-doc-app-pipes-decode-uri-component.mjs.map +1 -1
  92. package/fesm2022/ng-doc-app-pipes-extract-value.mjs +6 -4
  93. package/fesm2022/ng-doc-app-pipes-extract-value.mjs.map +1 -1
  94. package/fesm2022/ng-doc-app-pipes-filter-by-text.mjs +7 -4
  95. package/fesm2022/ng-doc-app-pipes-filter-by-text.mjs.map +1 -1
  96. package/fesm2022/ng-doc-app-pipes-sanitize-html.mjs +8 -5
  97. package/fesm2022/ng-doc-app-pipes-sanitize-html.mjs.map +1 -1
  98. package/fesm2022/ng-doc-app-processors-page-processor.mjs +86 -23
  99. package/fesm2022/ng-doc-app-processors-page-processor.mjs.map +1 -1
  100. package/fesm2022/ng-doc-app-processors-processors-tooltip.mjs +26 -25
  101. package/fesm2022/ng-doc-app-processors-processors-tooltip.mjs.map +1 -1
  102. package/fesm2022/ng-doc-app-processors-processors.mjs +42 -10
  103. package/fesm2022/ng-doc-app-processors-processors.mjs.map +1 -1
  104. package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs +31 -2
  105. package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs.map +1 -1
  106. package/fesm2022/ng-doc-app-providers-title.mjs +129 -0
  107. package/fesm2022/ng-doc-app-providers-title.mjs.map +1 -0
  108. package/fesm2022/ng-doc-app-providers.mjs +1 -0
  109. package/fesm2022/ng-doc-app-providers.mjs.map +1 -1
  110. package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs +122 -0
  111. package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs.map +1 -0
  112. package/fesm2022/ng-doc-app-services-content-state.mjs +57 -0
  113. package/fesm2022/ng-doc-app-services-content-state.mjs.map +1 -0
  114. package/fesm2022/ng-doc-app-services-fullscreen-route.mjs +74 -0
  115. package/fesm2022/ng-doc-app-services-fullscreen-route.mjs.map +1 -0
  116. package/fesm2022/ng-doc-app-services-highlighter.mjs +90 -17
  117. package/fesm2022/ng-doc-app-services-highlighter.mjs.map +1 -1
  118. package/fesm2022/ng-doc-app-services-route-preloader.mjs +384 -0
  119. package/fesm2022/ng-doc-app-services-route-preloader.mjs.map +1 -0
  120. package/fesm2022/ng-doc-app-services-shortcuts.mjs +214 -0
  121. package/fesm2022/ng-doc-app-services-shortcuts.mjs.map +1 -0
  122. package/fesm2022/ng-doc-app-services-sidebar.mjs +59 -25
  123. package/fesm2022/ng-doc-app-services-sidebar.mjs.map +1 -1
  124. package/fesm2022/ng-doc-app-services-store.mjs +8 -8
  125. package/fesm2022/ng-doc-app-services-store.mjs.map +1 -1
  126. package/fesm2022/ng-doc-app-services-theme.mjs +32 -12
  127. package/fesm2022/ng-doc-app-services-theme.mjs.map +1 -1
  128. package/fesm2022/ng-doc-app-services.mjs +143 -0
  129. package/fesm2022/ng-doc-app-services.mjs.map +1 -1
  130. package/fesm2022/ng-doc-app-tokens.mjs +7 -28
  131. package/fesm2022/ng-doc-app-tokens.mjs.map +1 -1
  132. package/fesm2022/ng-doc-app-type-controls-boolean-control.mjs +18 -9
  133. package/fesm2022/ng-doc-app-type-controls-boolean-control.mjs.map +1 -1
  134. package/fesm2022/ng-doc-app-type-controls-number-control.mjs +12 -9
  135. package/fesm2022/ng-doc-app-type-controls-number-control.mjs.map +1 -1
  136. package/fesm2022/ng-doc-app-type-controls-string-control.mjs +12 -9
  137. package/fesm2022/ng-doc-app-type-controls-string-control.mjs.map +1 -1
  138. package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs +59 -18
  139. package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs.map +1 -1
  140. package/package.json +82 -28
  141. package/styles/global.css +1128 -201
  142. package/styles/main.css +1128 -201
  143. package/styles/themes/dark.css +88 -56
  144. package/types/ng-doc-app-classes-content-anchor-controller.d.ts +54 -0
  145. package/types/ng-doc-app-classes-content-controller.d.ts +94 -0
  146. package/types/ng-doc-app-classes-default-search-engine.d.ts +9 -1
  147. package/types/ng-doc-app-classes-root-page.d.ts +14 -1
  148. package/types/ng-doc-app-classes-search-engine.d.ts +5 -0
  149. package/types/ng-doc-app-classes.d.ts +2 -0
  150. package/types/ng-doc-app-components-api-list.d.ts +69 -20
  151. package/types/ng-doc-app-components-breadcrumb.d.ts +8 -3
  152. package/types/ng-doc-app-components-code.d.ts +46 -12
  153. package/types/ng-doc-app-components-command-palette.d.ts +147 -0
  154. package/types/ng-doc-app-components-copy-button.d.ts +18 -6
  155. package/types/ng-doc-app-components-demo-displayer.d.ts +23 -10
  156. package/types/ng-doc-app-components-demo-frame.d.ts +48 -0
  157. package/types/ng-doc-app-components-demo-pane.d.ts +19 -15
  158. package/types/ng-doc-app-components-demo.d.ts +80 -15
  159. package/types/ng-doc-app-components-fullscreen-button.d.ts +4 -2
  160. package/types/ng-doc-app-components-fullscreen-toggle.d.ts +24 -0
  161. package/types/ng-doc-app-components-heading-anchor.d.ts +26 -7
  162. package/types/ng-doc-app-components-image-viewer.d.ts +71 -7
  163. package/types/ng-doc-app-components-kind-icon.d.ts +28 -7
  164. package/types/ng-doc-app-components-link.d.ts +6 -2
  165. package/types/ng-doc-app-components-members.d.ts +13 -0
  166. package/types/ng-doc-app-components-mermaid-viewer.d.ts +21 -6
  167. package/types/ng-doc-app-components-navbar.d.ts +25 -15
  168. package/types/ng-doc-app-components-page-header.d.ts +28 -4
  169. package/types/ng-doc-app-components-page-link.d.ts +34 -15
  170. package/types/ng-doc-app-components-page-navigation.d.ts +10 -5
  171. package/types/ng-doc-app-components-page-wrapper.d.ts +35 -11
  172. package/types/ng-doc-app-components-page.d.ts +57 -6
  173. package/types/ng-doc-app-components-playground.d.ts +167 -78
  174. package/types/ng-doc-app-components-root.d.ts +28 -7
  175. package/types/ng-doc-app-components-search-result.d.ts +14 -6
  176. package/types/ng-doc-app-components-search.d.ts +19 -7
  177. package/types/ng-doc-app-components-tabs.d.ts +24 -5
  178. package/types/ng-doc-app-components-theme-toggle.d.ts +18 -5
  179. package/types/ng-doc-app-components-toc.d.ts +76 -29
  180. package/types/ng-doc-app-components.d.ts +4 -1
  181. package/types/ng-doc-app-demo-app.d.ts +89 -0
  182. package/types/ng-doc-app-directives-code-highlighter.d.ts +8 -4
  183. package/types/ng-doc-app-directives-members-filter.d.ts +61 -0
  184. package/types/ng-doc-app-directives-route-active.d.ts +21 -7
  185. package/types/ng-doc-app-directives.d.ts +1 -0
  186. package/types/ng-doc-app-helpers.d.ts +142 -3
  187. package/types/ng-doc-app-interfaces.d.ts +62 -31
  188. package/types/ng-doc-app-pipes-decode-uri-component.d.ts +3 -0
  189. package/types/ng-doc-app-pipes-extract-value.d.ts +3 -0
  190. package/types/ng-doc-app-pipes-filter-by-text.d.ts +4 -0
  191. package/types/ng-doc-app-pipes-sanitize-html.d.ts +4 -2
  192. package/types/ng-doc-app-processors-page-processor.d.ts +39 -10
  193. package/types/ng-doc-app-processors-processors-tooltip.d.ts +11 -10
  194. package/types/ng-doc-app-processors-processors.d.ts +10 -1
  195. package/types/ng-doc-app-providers-ng-doc-app.d.ts +18 -1
  196. package/types/ng-doc-app-providers-title.d.ts +59 -0
  197. package/types/ng-doc-app-providers.d.ts +1 -0
  198. package/types/ng-doc-app-services-content-scroll-intent.d.ts +52 -0
  199. package/types/ng-doc-app-services-content-state.d.ts +40 -0
  200. package/types/ng-doc-app-services-fullscreen-route.d.ts +35 -0
  201. package/types/ng-doc-app-services-highlighter.d.ts +47 -5
  202. package/types/ng-doc-app-services-route-preloader.d.ts +121 -0
  203. package/types/ng-doc-app-services-shortcuts.d.ts +96 -0
  204. package/types/ng-doc-app-services-sidebar.d.ts +24 -4
  205. package/types/ng-doc-app-services-store.d.ts +24 -1
  206. package/types/ng-doc-app-services-theme.d.ts +19 -3
  207. package/types/ng-doc-app-services.d.ts +77 -0
  208. package/types/ng-doc-app-tokens.d.ts +10 -30
  209. package/types/ng-doc-app-type-controls-boolean-control.d.ts +11 -6
  210. package/types/ng-doc-app-type-controls-number-control.d.ts +5 -3
  211. package/types/ng-doc-app-type-controls-string-control.d.ts +5 -3
  212. package/types/ng-doc-app-type-controls-type-alias-control.d.ts +35 -6
  213. package/fesm2022/ng-doc-app-components-search-dialog.mjs +0 -78
  214. package/fesm2022/ng-doc-app-components-search-dialog.mjs.map +0 -1
  215. package/types/ng-doc-app-components-search-dialog.d.ts +0 -28
@@ -1,13 +1,19 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Input, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { input, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { RouterLink } from '@angular/router';
4
4
  import { NgDocTextComponent, NgDocIconComponent, NgDocTextRightDirective, NgDocButtonComponent } from '@ng-doc/ui-kit';
5
5
 
6
+ /** A link that opens a demo's fullscreen route in a new tab. */
6
7
  class NgDocFullscreenButtonComponent {
7
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocFullscreenButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.6", type: NgDocFullscreenButtonComponent, isStandalone: true, selector: "ng-doc-fullscreen-button", inputs: { route: "route" }, ngImport: i0, template: "<a ng-doc-button-text [routerLink]=\"route\" target=\"_blank\">\n\t<span ng-doc-text>\n\t\tOpen demo in a new tab\n\t\t<ng-doc-icon icon=\"external-link\" ngDocTextRight></ng-doc-icon>\n\t</span>\n</a>\n", styles: [":host{display:flex;align-items:center;justify-content:center;padding:calc(var(--ng-doc-base-gutter) * 2) calc(var(--ng-doc-base-gutter) * 2)}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: NgDocTextComponent, selector: "[ng-doc-text]", inputs: ["size", "color", "align", "absoluteContent"] }, { kind: "component", type: NgDocIconComponent, selector: "ng-doc-icon", inputs: ["icon", "customIcon", "size"] }, { kind: "directive", type: NgDocTextRightDirective, selector: "[ngDocTextRight]" }, { kind: "component", type: NgDocButtonComponent, selector: "button[ng-doc-button], a[ng-doc-button], button[ng-doc-button-flat], a[ng-doc-button-flat], button[ng-doc-button-text], a[ng-doc-button-text]", inputs: ["size", "color", "rounded"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
8
+ constructor() {
9
+ /** Route of the fullscreen demo. */
10
+ this.route = input.required(/* @ts-ignore */
11
+ ...(ngDevMode ? [{ debugName: "route" }] : /* istanbul ignore next */ []));
12
+ }
13
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocFullscreenButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
14
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.1", type: NgDocFullscreenButtonComponent, isStandalone: true, selector: "ng-doc-fullscreen-button", inputs: { route: { classPropertyName: "route", publicName: "route", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<a ng-doc-button-text [routerLink]=\"route()\" target=\"_blank\">\n <span ng-doc-text>\n Open demo in a new tab\n <ng-doc-icon icon=\"external-link\" ngDocTextRight></ng-doc-icon>\n </span>\n</a>\n", styles: [":host{display:flex;align-items:center;justify-content:center;padding:calc(var(--ng-doc-base-gutter) * 2) calc(var(--ng-doc-base-gutter) * 2)}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: NgDocTextComponent, selector: "[ng-doc-text]", inputs: ["size", "color", "align", "absoluteContent"] }, { kind: "component", type: NgDocIconComponent, selector: "ng-doc-icon", inputs: ["icon", "customIcon", "size"] }, { kind: "directive", type: NgDocTextRightDirective, selector: "[ngDocTextRight]" }, { kind: "component", type: NgDocButtonComponent, selector: "button[ng-doc-button], a[ng-doc-button], button[ng-doc-button-flat], a[ng-doc-button-flat], button[ng-doc-button-text], a[ng-doc-button-text]", inputs: ["size", "color", "rounded"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
9
15
  }
10
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocFullscreenButtonComponent, decorators: [{
16
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocFullscreenButtonComponent, decorators: [{
11
17
  type: Component,
12
18
  args: [{ selector: 'ng-doc-fullscreen-button', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
13
19
  RouterLink,
@@ -15,11 +21,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
15
21
  NgDocIconComponent,
16
22
  NgDocTextRightDirective,
17
23
  NgDocButtonComponent,
18
- ], template: "<a ng-doc-button-text [routerLink]=\"route\" target=\"_blank\">\n\t<span ng-doc-text>\n\t\tOpen demo in a new tab\n\t\t<ng-doc-icon icon=\"external-link\" ngDocTextRight></ng-doc-icon>\n\t</span>\n</a>\n", styles: [":host{display:flex;align-items:center;justify-content:center;padding:calc(var(--ng-doc-base-gutter) * 2) calc(var(--ng-doc-base-gutter) * 2)}\n"] }]
19
- }], propDecorators: { route: [{
20
- type: Input,
21
- args: [{ required: true }]
22
- }] } });
24
+ ], template: "<a ng-doc-button-text [routerLink]=\"route()\" target=\"_blank\">\n <span ng-doc-text>\n Open demo in a new tab\n <ng-doc-icon icon=\"external-link\" ngDocTextRight></ng-doc-icon>\n </span>\n</a>\n", styles: [":host{display:flex;align-items:center;justify-content:center;padding:calc(var(--ng-doc-base-gutter) * 2) calc(var(--ng-doc-base-gutter) * 2)}\n"] }]
25
+ }], propDecorators: { route: [{ type: i0.Input, args: [{ isSignal: true, alias: "route", required: true }] }] } });
23
26
 
24
27
  /**
25
28
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"ng-doc-app-components-fullscreen-button.mjs","sources":["../../../../libs/app/components/fullscreen-button/fullscreen-button.component.ts","../../../../libs/app/components/fullscreen-button/fullscreen-button.component.html","../../../../libs/app/components/fullscreen-button/ng-doc-app-components-fullscreen-button.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, Input } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport {\n NgDocButtonComponent,\n NgDocIconComponent,\n NgDocTextComponent,\n NgDocTextRightDirective,\n} from '@ng-doc/ui-kit';\n\n@Component({\n selector: 'ng-doc-fullscreen-button',\n templateUrl: './fullscreen-button.component.html',\n styleUrls: ['./fullscreen-button.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n RouterLink,\n NgDocTextComponent,\n NgDocIconComponent,\n NgDocTextRightDirective,\n NgDocButtonComponent,\n ],\n})\nexport class NgDocFullscreenButtonComponent {\n @Input({ required: true })\n route!: string;\n}\n","<a ng-doc-button-text [routerLink]=\"route\" target=\"_blank\">\n\t<span ng-doc-text>\n\t\tOpen demo in a new tab\n\t\t<ng-doc-icon icon=\"external-link\" ngDocTextRight></ng-doc-icon>\n\t</span>\n</a>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;MAsBa,8BAA8B,CAAA;8GAA9B,8BAA8B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;kGAA9B,8BAA8B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,0BAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,OAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECtB3C,6MAMA,EAAA,MAAA,EAAA,CAAA,iJAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDSI,UAAU,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,aAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,OAAA,EAAA,MAAA,EAAA,YAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,YAAA,EAAA,YAAA,EAAA,YAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACV,kBAAkB,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,OAAA,EAAA,OAAA,EAAA,iBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAClB,kBAAkB,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,YAAA,EAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAClB,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACvB,oBAAoB,EAAA,QAAA,EAAA,+IAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,OAAA,EAAA,SAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;2FAGX,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBAb1C,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,0BAA0B,EAAA,eAAA,EAGnB,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC;wBACP,UAAU;wBACV,kBAAkB;wBAClB,kBAAkB;wBAClB,uBAAuB;wBACvB,oBAAoB;AACrB,qBAAA,EAAA,QAAA,EAAA,6MAAA,EAAA,MAAA,EAAA,CAAA,iJAAA,CAAA,EAAA;;sBAGA,KAAK;uBAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;;;AEvB3B;;AAEG;;;;"}
1
+ {"version":3,"file":"ng-doc-app-components-fullscreen-button.mjs","sources":["../../../../libs/app/components/fullscreen-button/fullscreen-button.component.ts","../../../../libs/app/components/fullscreen-button/fullscreen-button.component.html","../../../../libs/app/components/fullscreen-button/ng-doc-app-components-fullscreen-button.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, input } from '@angular/core';\nimport { RouterLink } from '@angular/router';\nimport {\n NgDocButtonComponent,\n NgDocIconComponent,\n NgDocTextComponent,\n NgDocTextRightDirective,\n} from '@ng-doc/ui-kit';\n\n/** A link that opens a demo's fullscreen route in a new tab. */\n@Component({\n selector: 'ng-doc-fullscreen-button',\n templateUrl: './fullscreen-button.component.html',\n styleUrls: ['./fullscreen-button.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n RouterLink,\n NgDocTextComponent,\n NgDocIconComponent,\n NgDocTextRightDirective,\n NgDocButtonComponent,\n ],\n})\nexport class NgDocFullscreenButtonComponent {\n /** Route of the fullscreen demo. */\n readonly route = input.required<string>();\n}\n","<a ng-doc-button-text [routerLink]=\"route()\" target=\"_blank\">\n <span ng-doc-text>\n Open demo in a new tab\n <ng-doc-icon icon=\"external-link\" ngDocTextRight></ng-doc-icon>\n </span>\n</a>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AASA;MAca,8BAA8B,CAAA;AAb3C,IAAA,WAAA,GAAA;;QAeW,IAAA,CAAA,KAAK,GAAG,KAAK,CAAC,QAAQ;kFAAU;AAC1C,IAAA;8GAHY,8BAA8B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;kGAA9B,8BAA8B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,0BAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECvB3C,+MAMA,EAAA,MAAA,EAAA,CAAA,iJAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EDUI,UAAU,EAAA,QAAA,EAAA,cAAA,EAAA,MAAA,EAAA,CAAA,QAAA,EAAA,aAAA,EAAA,UAAA,EAAA,qBAAA,EAAA,OAAA,EAAA,MAAA,EAAA,YAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,YAAA,EAAA,YAAA,EAAA,YAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACV,kBAAkB,EAAA,QAAA,EAAA,eAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,OAAA,EAAA,OAAA,EAAA,iBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAClB,kBAAkB,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,YAAA,EAAA,MAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAClB,uBAAuB,EAAA,QAAA,EAAA,kBAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EACvB,oBAAoB,EAAA,QAAA,EAAA,+IAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,OAAA,EAAA,SAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;2FAGX,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBAb1C,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,0BAA0B,EAAA,eAAA,EAGnB,uBAAuB,CAAC,MAAM,EAAA,OAAA,EACtC;wBACP,UAAU;wBACV,kBAAkB;wBAClB,kBAAkB;wBAClB,uBAAuB;wBACvB,oBAAoB;AACrB,qBAAA,EAAA,QAAA,EAAA,+MAAA,EAAA,MAAA,EAAA,CAAA,iJAAA,CAAA,EAAA;;;AErBH;;AAEG;;;;"}
@@ -0,0 +1,79 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { NgDocButtonIconComponent, NgDocTooltipDirective } from '@ng-doc/ui-kit';
4
+
5
+ /**
6
+ * An icon button that shows an element fullscreen with the browser Fullscreen API, or leaves
7
+ * fullscreen. Its icon, label and tooltip follow the fullscreen state, and it is hidden where the
8
+ * browser does not support fullscreen and on the server.
9
+ * @example
10
+ * ```html
11
+ * <div ngDocFullscreen #stage="ngDocFullscreen">…</div>
12
+ * <ng-doc-fullscreen-toggle [target]="stage" />
13
+ * ```
14
+ */
15
+ class NgDocFullscreenToggleComponent {
16
+ constructor() {
17
+ /** The element to show fullscreen. */
18
+ this.target = input.required(/* @ts-ignore */
19
+ ...(ngDevMode ? [{ debugName: "target" }] : /* istanbul ignore next */ []));
20
+ /** Accessible name and tooltip of the button, which follow the fullscreen state. */
21
+ this.label = computed(() => this.target().active() ? 'Exit fullscreen' : 'Fullscreen', /* @ts-ignore */
22
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
23
+ }
24
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocFullscreenToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
25
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.1", type: NgDocFullscreenToggleComponent, isStandalone: true, selector: "ng-doc-fullscreen-toggle", inputs: { target: { classPropertyName: "target", publicName: "target", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "hidden": "!target().supported()" } }, ngImport: i0, template: `
26
+ @if (target().supported()) {
27
+ <button
28
+ type="button"
29
+ ng-doc-button-icon
30
+ [rounded]="false"
31
+ [attr.aria-label]="label()"
32
+ [ngDocTooltip]="label()"
33
+ (click)="target().toggle()">
34
+ <svg viewBox="0 0 24 24" aria-hidden="true">
35
+ @if (target().active()) {
36
+ <path
37
+ d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3" />
38
+ } @else {
39
+ <path
40
+ d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
41
+ }
42
+ </svg>
43
+ </button>
44
+ }
45
+ `, isInline: true, styles: [":host{display:inline-flex}:host([hidden]){display:none}svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}\n"], dependencies: [{ kind: "component", type: NgDocButtonIconComponent, selector: "button[ng-doc-button-icon], a[ng-doc-button-icon], button[ng-doc-button-icon-raised], a[ng-doc-button-icon-raised], button[ng-doc-button-icon-transparent], a[ng-doc-button-icon-transparent]", inputs: ["size", "rounded"] }, { kind: "directive", type: NgDocTooltipDirective, selector: "[ngDocTooltip]", inputs: ["ngDocTooltip", "delay", "displayOrigin", "pointerOrigin", "positions", "canOpen", "panelClass", "minHeight", "maxHeight", "height", "minWidth", "maxWidth", "width"], outputs: ["beforeOpen", "afterOpen", "beforeClose", "afterClose"], exportAs: ["ngDocTooltip"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
46
+ }
47
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocFullscreenToggleComponent, decorators: [{
48
+ type: Component,
49
+ args: [{ selector: 'ng-doc-fullscreen-toggle', imports: [NgDocButtonIconComponent, NgDocTooltipDirective], template: `
50
+ @if (target().supported()) {
51
+ <button
52
+ type="button"
53
+ ng-doc-button-icon
54
+ [rounded]="false"
55
+ [attr.aria-label]="label()"
56
+ [ngDocTooltip]="label()"
57
+ (click)="target().toggle()">
58
+ <svg viewBox="0 0 24 24" aria-hidden="true">
59
+ @if (target().active()) {
60
+ <path
61
+ d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3" />
62
+ } @else {
63
+ <path
64
+ d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3" />
65
+ }
66
+ </svg>
67
+ </button>
68
+ }
69
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
70
+ '[hidden]': '!target().supported()',
71
+ }, styles: [":host{display:inline-flex}:host([hidden]){display:none}svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}\n"] }]
72
+ }], propDecorators: { target: [{ type: i0.Input, args: [{ isSignal: true, alias: "target", required: true }] }] } });
73
+
74
+ /**
75
+ * Generated bundle index. Do not edit.
76
+ */
77
+
78
+ export { NgDocFullscreenToggleComponent };
79
+ //# sourceMappingURL=ng-doc-app-components-fullscreen-toggle.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ng-doc-app-components-fullscreen-toggle.mjs","sources":["../../../../libs/app/components/fullscreen-toggle/fullscreen-toggle.component.ts","../../../../libs/app/components/fullscreen-toggle/ng-doc-app-components-fullscreen-toggle.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, input, Signal } from '@angular/core';\nimport {\n NgDocButtonIconComponent,\n NgDocFullscreenDirective,\n NgDocTooltipDirective,\n} from '@ng-doc/ui-kit';\n\n/**\n * An icon button that shows an element fullscreen with the browser Fullscreen API, or leaves\n * fullscreen. Its icon, label and tooltip follow the fullscreen state, and it is hidden where the\n * browser does not support fullscreen and on the server.\n * @example\n * ```html\n * <div ngDocFullscreen #stage=\"ngDocFullscreen\">…</div>\n * <ng-doc-fullscreen-toggle [target]=\"stage\" />\n * ```\n */\n@Component({\n selector: 'ng-doc-fullscreen-toggle',\n imports: [NgDocButtonIconComponent, NgDocTooltipDirective],\n template: `\n @if (target().supported()) {\n <button\n type=\"button\"\n ng-doc-button-icon\n [rounded]=\"false\"\n [attr.aria-label]=\"label()\"\n [ngDocTooltip]=\"label()\"\n (click)=\"target().toggle()\">\n <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\">\n @if (target().active()) {\n <path\n d=\"M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3\" />\n } @else {\n <path\n d=\"M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3\" />\n }\n </svg>\n </button>\n }\n `,\n styles: `\n :host {\n display: inline-flex;\n }\n\n :host([hidden]) {\n display: none;\n }\n\n svg {\n width: 16px;\n height: 16px;\n fill: none;\n stroke: currentColor;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n }\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[hidden]': '!target().supported()',\n },\n})\nexport class NgDocFullscreenToggleComponent {\n /** The element to show fullscreen. */\n readonly target = input.required<NgDocFullscreenDirective>();\n\n /** Accessible name and tooltip of the button, which follow the fullscreen state. */\n protected readonly label: Signal<string> = computed(() =>\n this.target().active() ? 'Exit fullscreen' : 'Fullscreen',\n );\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAOA;;;;;;;;;AASG;MAiDU,8BAA8B,CAAA;AAhD3C,IAAA,WAAA,GAAA;;QAkDW,IAAA,CAAA,MAAM,GAAG,KAAK,CAAC,QAAQ;mFAA4B;;QAGzC,IAAA,CAAA,KAAK,GAAmB,QAAQ,CAAC,MAClD,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,GAAG,iBAAiB,GAAG,YAAY;kFAC1D;AACF,IAAA;8GARY,8BAA8B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAA9B,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,8BAA8B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,0BAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA7C/B;;;;;;;;;;;;;;;;;;;;GAoBT,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,8KAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EArBS,wBAAwB,uQAAE,qBAAqB,EAAA,QAAA,EAAA,gBAAA,EAAA,MAAA,EAAA,CAAA,cAAA,EAAA,OAAA,EAAA,eAAA,EAAA,eAAA,EAAA,WAAA,EAAA,SAAA,EAAA,YAAA,EAAA,WAAA,EAAA,WAAA,EAAA,QAAA,EAAA,UAAA,EAAA,UAAA,EAAA,OAAA,CAAA,EAAA,OAAA,EAAA,CAAA,YAAA,EAAA,WAAA,EAAA,aAAA,EAAA,YAAA,CAAA,EAAA,QAAA,EAAA,CAAA,cAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;2FA8C9C,8BAA8B,EAAA,UAAA,EAAA,CAAA;kBAhD1C,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,0BAA0B,WAC3B,CAAC,wBAAwB,EAAE,qBAAqB,CAAC,EAAA,QAAA,EAChD;;;;;;;;;;;;;;;;;;;;GAoBT,EAAA,eAAA,EAoBgB,uBAAuB,CAAC,MAAM,EAAA,IAAA,EACzC;AACJ,wBAAA,UAAU,EAAE,uBAAuB;AACpC,qBAAA,EAAA,MAAA,EAAA,CAAA,8KAAA,CAAA,EAAA;;;AC/DH;;AAEG;;;;"}
@@ -1,41 +1,74 @@
1
+ import { LocationStrategy } from '@angular/common';
1
2
  import * as i0 from '@angular/core';
2
- import { inject, Input, HostBinding, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { input, inject, ElementRef, Renderer2, ChangeDetectionStrategy, Component } from '@angular/core';
3
4
  import { NgDocCopyButtonComponent } from '@ng-doc/app/components/copy-button';
5
+ import { ɵngDocPageUrl as _ngDocPageUrl } from '@ng-doc/app/helpers';
4
6
  import { NgDocIconComponent } from '@ng-doc/ui-kit';
5
7
  import { WA_LOCATION } from '@ng-web-apis/common';
6
8
 
9
+ /**
10
+ * Button next to a heading that copies the link to the heading.
11
+ */
7
12
  class NgDocHeadingAnchorComponent {
8
13
  constructor() {
9
- this.classes = [];
14
+ /** Id of the heading. */
15
+ this.anchor = input.required(/* @ts-ignore */
16
+ ...(ngDevMode ? [{ debugName: "anchor" }] : /* istanbul ignore next */ []));
17
+ /** Classes of the host element. */
18
+ this.classes = input([], /* @ts-ignore */
19
+ ...(ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []));
10
20
  this.location = inject(WA_LOCATION);
21
+ this.locationStrategy = inject(LocationStrategy);
22
+ this.host = inject(ElementRef).nativeElement;
23
+ this.renderer = inject(Renderer2);
24
+ // Read when the button is pressed: the location is not a signal, and the link must use the
25
+ // route the reader is on, in the form the location strategy shows it.
26
+ this.href = () => _ngDocPageUrl(this.locationStrategy, this.location, this.anchor());
11
27
  }
12
- get href() {
13
- const { origin, pathname } = this.location;
14
- return `${origin}${pathname}#${this.anchor}`;
28
+ /**
29
+ * Names the heading after its own text: the anchor sits inside the heading, so the button's
30
+ * label would otherwise end the heading's accessible name ("Installation Copy link to
31
+ * section"). The heading's other content moves into a span that the heading is labelled by, so
32
+ * the name follows that content and leaves out whatever it hides from assistive technology.
33
+ * The processors insert the anchor before its first change detection, on the server as in the
34
+ * browser, so the heading is its parent here.
35
+ * @internal
36
+ */
37
+ ngOnInit() {
38
+ const heading = this.host.parentElement;
39
+ if (!heading ||
40
+ !/^H[1-6]$/.test(heading.tagName) ||
41
+ heading.hasAttribute('aria-label') ||
42
+ heading.hasAttribute('aria-labelledby'))
43
+ return;
44
+ const id = `ng-doc-heading-label-${heading.id || this.anchor()}`;
45
+ const label = this.renderer.createElement('span');
46
+ this.renderer.setAttribute(label, 'id', id);
47
+ this.renderer.setAttribute(label, 'class', 'ng-doc-heading-label');
48
+ for (const node of Array.from(heading.childNodes)) {
49
+ if (node !== this.host)
50
+ this.renderer.appendChild(label, node);
51
+ }
52
+ this.renderer.insertBefore(heading, label, this.host);
53
+ this.renderer.setAttribute(heading, 'aria-labelledby', id);
15
54
  }
16
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocHeadingAnchorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
17
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.6", type: NgDocHeadingAnchorComponent, isStandalone: true, selector: "ng-doc-heading-anchor", inputs: { anchor: "anchor", classes: "classes" }, host: { properties: { "class": "this.classes" } }, ngImport: i0, template: `
18
- <ng-doc-copy-button [text]="href">
55
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocHeadingAnchorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
56
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.2.1", type: NgDocHeadingAnchorComponent, isStandalone: true, selector: "ng-doc-heading-anchor", inputs: { anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: true, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "classes()" } }, ngImport: i0, template: `
57
+ <ng-doc-copy-button [text]="href" label="Copy link to section">
19
58
  <ng-doc-icon icon="link-2"></ng-doc-icon>
20
59
  </ng-doc-copy-button>
21
- `, isInline: true, styles: [""], dependencies: [{ kind: "component", type: NgDocIconComponent, selector: "ng-doc-icon", inputs: ["icon", "customIcon", "size"] }, { kind: "component", type: NgDocCopyButtonComponent, selector: "ng-doc-copy-button", inputs: ["text"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
60
+ `, isInline: true, styles: [""], dependencies: [{ kind: "component", type: NgDocIconComponent, selector: "ng-doc-icon", inputs: ["icon", "customIcon", "size"] }, { kind: "component", type: NgDocCopyButtonComponent, selector: "ng-doc-copy-button", inputs: ["text", "label"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
22
61
  }
23
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocHeadingAnchorComponent, decorators: [{
62
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocHeadingAnchorComponent, decorators: [{
24
63
  type: Component,
25
64
  args: [{ selector: 'ng-doc-heading-anchor', imports: [NgDocIconComponent, NgDocCopyButtonComponent], template: `
26
- <ng-doc-copy-button [text]="href">
65
+ <ng-doc-copy-button [text]="href" label="Copy link to section">
27
66
  <ng-doc-icon icon="link-2"></ng-doc-icon>
28
67
  </ng-doc-copy-button>
29
- `, changeDetection: ChangeDetectionStrategy.OnPush }]
30
- }], propDecorators: { anchor: [{
31
- type: Input,
32
- args: [{ required: true }]
33
- }], classes: [{
34
- type: Input
35
- }, {
36
- type: HostBinding,
37
- args: ['class']
38
- }] } });
68
+ `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
69
+ '[class]': 'classes()',
70
+ } }]
71
+ }], propDecorators: { anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: true }] }], classes: [{ type: i0.Input, args: [{ isSignal: true, alias: "classes", required: false }] }] } });
39
72
 
40
73
  /**
41
74
  * Generated bundle index. Do not edit.
@@ -1 +1 @@
1
- {"version":3,"file":"ng-doc-app-components-heading-anchor.mjs","sources":["../../../../libs/app/components/heading-anchor/heading-anchor.component.ts","../../../../libs/app/components/heading-anchor/ng-doc-app-components-heading-anchor.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, HostBinding, inject, Input } from '@angular/core';\nimport { NgDocCopyButtonComponent } from '@ng-doc/app/components/copy-button';\nimport { NgDocIconComponent } from '@ng-doc/ui-kit';\nimport { WA_LOCATION } from '@ng-web-apis/common';\n\n@Component({\n selector: 'ng-doc-heading-anchor',\n imports: [NgDocIconComponent, NgDocCopyButtonComponent],\n template: `\n <ng-doc-copy-button [text]=\"href\">\n <ng-doc-icon icon=\"link-2\"></ng-doc-icon>\n </ng-doc-copy-button>\n `,\n styles: ``,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class NgDocHeadingAnchorComponent {\n @Input({ required: true })\n anchor!: string;\n\n @Input()\n @HostBinding('class')\n classes: string[] = [];\n\n protected readonly location = inject(WA_LOCATION);\n\n protected get href(): string {\n const { origin, pathname } = this.location;\n\n return `${origin}${pathname}#${this.anchor}`;\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;MAgBa,2BAA2B,CAAA;AAXxC,IAAA,WAAA,GAAA;QAiBE,IAAA,CAAA,OAAO,GAAa,EAAE;AAEH,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC;AAOlD,IAAA;AALC,IAAA,IAAc,IAAI,GAAA;QAChB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,QAAQ;QAE1C,OAAO,CAAA,EAAG,MAAM,CAAA,EAAG,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAA,CAAE;IAC9C;8GAdW,2BAA2B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAA3B,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,2BAA2B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,cAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAR5B;;;;GAIT,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EALS,kBAAkB,gGAAE,wBAAwB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;2FAS3C,2BAA2B,EAAA,UAAA,EAAA,CAAA;kBAXvC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,uBAAuB,WACxB,CAAC,kBAAkB,EAAE,wBAAwB,CAAC,EAAA,QAAA,EAC7C;;;;GAIT,EAAA,eAAA,EAEgB,uBAAuB,CAAC,MAAM,EAAA;;sBAG9C,KAAK;uBAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;;sBAGxB;;sBACA,WAAW;uBAAC,OAAO;;;ACrBtB;;AAEG;;;;"}
1
+ {"version":3,"file":"ng-doc-app-components-heading-anchor.mjs","sources":["../../../../libs/app/components/heading-anchor/heading-anchor.component.ts","../../../../libs/app/components/heading-anchor/ng-doc-app-components-heading-anchor.ts"],"sourcesContent":["import { LocationStrategy } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n inject,\n input,\n OnInit,\n Renderer2,\n} from '@angular/core';\nimport { NgDocCopyButtonComponent } from '@ng-doc/app/components/copy-button';\nimport { ɵngDocPageUrl } from '@ng-doc/app/helpers';\nimport { NgDocIconComponent } from '@ng-doc/ui-kit';\nimport { WA_LOCATION } from '@ng-web-apis/common';\n\n/**\n * Button next to a heading that copies the link to the heading.\n */\n@Component({\n selector: 'ng-doc-heading-anchor',\n imports: [NgDocIconComponent, NgDocCopyButtonComponent],\n template: `\n <ng-doc-copy-button [text]=\"href\" label=\"Copy link to section\">\n <ng-doc-icon icon=\"link-2\"></ng-doc-icon>\n </ng-doc-copy-button>\n `,\n styles: ``,\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: {\n '[class]': 'classes()',\n },\n})\nexport class NgDocHeadingAnchorComponent implements OnInit {\n /** Id of the heading. */\n readonly anchor = input.required<string>();\n\n /** Classes of the host element. */\n readonly classes = input<string[]>([]);\n\n protected readonly location = inject(WA_LOCATION);\n\n private readonly locationStrategy = inject(LocationStrategy);\n private readonly host: HTMLElement = inject(ElementRef).nativeElement;\n private readonly renderer = inject(Renderer2);\n\n // Read when the button is pressed: the location is not a signal, and the link must use the\n // route the reader is on, in the form the location strategy shows it.\n protected readonly href = (): string =>\n ɵngDocPageUrl(this.locationStrategy, this.location, this.anchor());\n\n /**\n * Names the heading after its own text: the anchor sits inside the heading, so the button's\n * label would otherwise end the heading's accessible name (\"Installation Copy link to\n * section\"). The heading's other content moves into a span that the heading is labelled by, so\n * the name follows that content and leaves out whatever it hides from assistive technology.\n * The processors insert the anchor before its first change detection, on the server as in the\n * browser, so the heading is its parent here.\n * @internal\n */\n ngOnInit(): void {\n const heading = this.host.parentElement;\n\n if (\n !heading ||\n !/^H[1-6]$/.test(heading.tagName) ||\n heading.hasAttribute('aria-label') ||\n heading.hasAttribute('aria-labelledby')\n )\n return;\n\n const id = `ng-doc-heading-label-${heading.id || this.anchor()}`;\n const label: HTMLElement = this.renderer.createElement('span');\n\n this.renderer.setAttribute(label, 'id', id);\n this.renderer.setAttribute(label, 'class', 'ng-doc-heading-label');\n for (const node of Array.from(heading.childNodes)) {\n if (node !== this.host) this.renderer.appendChild(label, node);\n }\n this.renderer.insertBefore(heading, label, this.host);\n this.renderer.setAttribute(heading, 'aria-labelledby', id);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["ɵngDocPageUrl"],"mappings":";;;;;;;;AAeA;;AAEG;MAeU,2BAA2B,CAAA;AAdxC,IAAA,WAAA,GAAA;;QAgBW,IAAA,CAAA,MAAM,GAAG,KAAK,CAAC,QAAQ;mFAAU;;QAGjC,IAAA,CAAA,OAAO,GAAG,KAAK,CAAW,EAAE;oFAAC;AAEnB,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC;AAEhC,QAAA,IAAA,CAAA,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,CAAC;AAC3C,QAAA,IAAA,CAAA,IAAI,GAAgB,MAAM,CAAC,UAAU,CAAC,CAAC,aAAa;AACpD,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,SAAS,CAAC;;;AAI1B,QAAA,IAAA,CAAA,IAAI,GAAG,MACxBA,aAAa,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;AAiCrE,IAAA;AA/BC;;;;;;;;AAQG;IACH,QAAQ,GAAA;AACN,QAAA,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa;AAEvC,QAAA,IACE,CAAC,OAAO;AACR,YAAA,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;AACjC,YAAA,OAAO,CAAC,YAAY,CAAC,YAAY,CAAC;AAClC,YAAA,OAAO,CAAC,YAAY,CAAC,iBAAiB,CAAC;YAEvC;AAEF,QAAA,MAAM,EAAE,GAAG,CAAA,qBAAA,EAAwB,OAAO,CAAC,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAA,CAAE;QAChE,MAAM,KAAK,GAAgB,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC;QAE9D,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QAC3C,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,sBAAsB,CAAC;AAClE,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE;AACjD,YAAA,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI;gBAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,EAAE,IAAI,CAAC;QAChE;AACA,QAAA,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC;QACrD,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE,iBAAiB,EAAE,EAAE,CAAC;IAC5D;8GAhDW,2BAA2B,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA,CAAA;AAA3B,IAAA,SAAA,IAAA,CAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,2BAA2B,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,OAAA,EAAA,WAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAX5B;;;;GAIT,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,EAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EALS,kBAAkB,gGAAE,wBAAwB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA,CAAA;;2FAY3C,2BAA2B,EAAA,UAAA,EAAA,CAAA;kBAdvC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,uBAAuB,WACxB,CAAC,kBAAkB,EAAE,wBAAwB,CAAC,EAAA,QAAA,EAC7C;;;;GAIT,EAAA,eAAA,EAEgB,uBAAuB,CAAC,MAAM,EAAA,IAAA,EACzC;AACJ,wBAAA,SAAS,EAAE,WAAW;AACvB,qBAAA,EAAA;;;AC9BH;;AAEG;;;;"}
@@ -1,27 +1,89 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, viewChild, TemplateRef, inject, ElementRef, ChangeDetectorRef, HostListener, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { input, viewChild, TemplateRef, inject, ElementRef, DOCUMENT, signal, DestroyRef, untracked, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { NgDocOverlayService, NgDocOverlayContainerComponent } from '@ng-doc/ui-kit';
4
4
 
5
+ /** Easing of the zoom: fast at first, settling into the final size. */
6
+ const ZOOM_EASING = 'cubic-bezier(0.2, 0, 0, 1)';
7
+ /**
8
+ * Wraps an image of the page: clicking it, or pressing Enter or Space on it, opens the image at
9
+ * its natural size, scaled down to fit the window.
10
+ *
11
+ * An image inside a link keeps the link's behaviour, and an image with an empty `alt` is
12
+ * decorative: neither becomes a button.
13
+ */
5
14
  class NgDocImageViewerComponent {
6
15
  constructor() {
16
+ /** Source of the image. */
7
17
  this.src = input.required(/* @ts-ignore */
8
18
  ...(ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []));
19
+ /** Alternative text of the image. */
9
20
  this.alt = input.required(/* @ts-ignore */
10
21
  ...(ngDevMode ? [{ debugName: "alt" }] : /* istanbul ignore next */ []));
11
- this.image = viewChild('image', { ...(ngDevMode ? { debugName: "image" } : /* istanbul ignore next */ {}), read: TemplateRef });
22
+ this.image = viewChild.required('image', { ...(ngDevMode ? { debugName: "image" } : /* istanbul ignore next */ {}), read: TemplateRef });
12
23
  this.overlay = inject(NgDocOverlayService);
13
24
  this.element = inject((ElementRef)).nativeElement;
14
- this.changeDetectorRef = inject(ChangeDetectorRef);
25
+ this.window = inject(DOCUMENT).defaultView;
26
+ /** Whether the scaled image is open. */
27
+ this.opened = signal(false, /* @ts-ignore */
28
+ ...(ngDevMode ? [{ debugName: "opened" }] : /* istanbul ignore next */ []));
29
+ /** The size of the opened image. */
30
+ this.size = signal({ width: 0, height: 0 }, /* @ts-ignore */
31
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
32
+ this.attempt = 0;
33
+ this.destroyed = false;
34
+ inject(DestroyRef).onDestroy(() => {
35
+ this.destroyed = true;
36
+ this.overlayRef?.close();
37
+ });
15
38
  }
39
+ /**
40
+ * Opens the image at its natural size, scaled down to fit the window, unless the image is not
41
+ * interactive. The image grows from its place on the page.
42
+ */
16
43
  clickEvent() {
44
+ untracked(() => {
45
+ if (this.isInteractive()) {
46
+ void this.open();
47
+ }
48
+ });
49
+ }
50
+ /**
51
+ * Whether the image opens: it has a text alternative and is not inside a link. The page
52
+ * processor moves the host into place before change detection, so the check reads the DOM
53
+ * where the image ends up.
54
+ */
55
+ isInteractive() {
56
+ return !!this.alt().trim() && !this.element.parentElement?.closest('a');
57
+ }
58
+ /**
59
+ * Opens the image on Space without scrolling the page.
60
+ * @param event - The key press.
61
+ */
62
+ onSpace(event) {
63
+ if (this.isInteractive()) {
64
+ event.preventDefault();
65
+ this.clickEvent();
66
+ }
67
+ }
68
+ async open() {
17
69
  this.overlayRef?.close();
18
- const { width, height, top, left } = this.element.getBoundingClientRect();
19
- const windowWidth = window.innerWidth;
20
- const windowHeight = window.innerHeight;
21
- const ratio = Math.min(windowWidth / width, windowHeight / height);
22
- const newWidth = width * ratio;
23
- const newHeight = height * ratio;
24
- this.overlayRef = this.overlay.open(this.image(), {
70
+ const attempt = ++this.attempt;
71
+ const thumbnail = this.element.querySelector('img');
72
+ await this.decode(thumbnail);
73
+ // The reader may have opened the image again, pressed Escape or left the page while it
74
+ // decoded.
75
+ if (attempt !== this.attempt || this.destroyed) {
76
+ return;
77
+ }
78
+ // The size is known before anything moves, so the image grows straight to it.
79
+ const source = this.sourceSize(thumbnail);
80
+ const size = this.fit(source);
81
+ const zoomedFrame = { transformOrigin: '0 0', transform: 'none' };
82
+ // The close animation reads its keyframes when it runs; a refit updates them in place.
83
+ const closeFrames = [zoomedFrame, this.thumbnailFrame(thumbnail, size)];
84
+ const animate = !this.window?.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
85
+ this.size.set(size);
86
+ const overlayRef = this.overlay.open(this.image(), {
25
87
  overlayContainer: NgDocOverlayContainerComponent,
26
88
  positionStrategy: this.overlay
27
89
  .globalPositionStrategy()
@@ -30,92 +92,124 @@ class NgDocImageViewerComponent {
30
92
  scrollStrategy: this.overlay.scrollStrategy().block(),
31
93
  hasBackdrop: true,
32
94
  backdropClass: 'ng-doc-blur-backdrop',
33
- openAnimation: [
34
- [
35
- {
36
- position: 'fixed',
37
- width: `${width}px`,
38
- height: `${height}px`,
39
- top: `${top}px`,
40
- left: `${left}px`,
41
- transform: 'translate(0%, 0)',
42
- },
43
- {
44
- position: 'fixed',
45
- width: `${newWidth}px`,
46
- height: `${newHeight}px`,
47
- top: '50%',
48
- left: '50%',
49
- transform: 'translate(-50%, -50%)',
50
- },
51
- ],
52
- {
53
- duration: 300,
54
- easing: 'cubic-bezier(0.25, 0.8, 0.25, 1)',
55
- },
56
- //
57
- // group([
58
- // animate(
59
- // '300ms cubic-bezier(0.25, 0.8, 0.25, 1)',
60
- // style({
61
- // width: `${newWidth}px`,
62
- // height: `${newHeight}px`,
63
- // top: '50%',
64
- // left: '50%',
65
- // transform: 'translate(-50%, -50%)',
66
- // }),
67
- // ),
68
- // query('.ng-doc-image-container', [
69
- // style({ padding: 0 }),
70
- // animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({ padding: '16px' })),
71
- // ]),
72
- // ]),
73
- ],
74
- // closeAnimation: [
75
- // style({
76
- // position: 'fixed',
77
- // width: `${newWidth}px`,
78
- // height: `${newHeight}px`,
79
- // top: '50%',
80
- // left: '50%',
81
- // transform: 'translate(-50%, -50%)',
82
- // }),
83
- // group([
84
- // animate(
85
- // '300ms cubic-bezier(0.25, 0.8, 0.25, 1)',
86
- // style({ width, height, top, left, transform: 'translate(0%, 0)' }),
87
- // ),
88
- // query(
89
- // '.ng-doc-image-container',
90
- // animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({ padding: 0 })),
91
- // ),
92
- // ]),
93
- // ],
95
+ width: `${size.width}px`,
96
+ height: `${size.height}px`,
97
+ openAnimation: animate
98
+ ? [[closeFrames[1], zoomedFrame], { duration: 300, easing: ZOOM_EASING }]
99
+ : undefined,
100
+ closeAnimation: animate
101
+ ? [closeFrames, { duration: 200, easing: ZOOM_EASING, fill: 'forwards' }]
102
+ : undefined,
94
103
  });
95
- this.overlayRef.afterClose().subscribe(() => {
96
- this.overlayRef = undefined;
97
- this.changeDetectorRef.markForCheck();
104
+ const listeners = new AbortController();
105
+ // A resized or rotated window fits the image again.
106
+ const refit = () => {
107
+ const fitted = this.fit(source);
108
+ this.size.set(fitted);
109
+ overlayRef.overlayRef.updateSize({
110
+ width: `${fitted.width}px`,
111
+ height: `${fitted.height}px`,
112
+ });
113
+ closeFrames[1] = this.thumbnailFrame(thumbnail, fitted);
114
+ };
115
+ this.window?.addEventListener('resize', refit, { signal: listeners.signal });
116
+ this.window?.addEventListener('orientationchange', refit, { signal: listeners.signal });
117
+ this.overlayRef = overlayRef;
118
+ this.opened.set(true);
119
+ overlayRef.afterClose().subscribe(() => {
120
+ listeners.abort();
121
+ if (this.overlayRef === overlayRef) {
122
+ this.overlayRef = undefined;
123
+ this.opened.set(false);
124
+ this.element.focus();
125
+ }
98
126
  });
99
127
  }
100
- enterContainer(element) {
101
- console.log('enterContainer');
102
- element.animate([{ padding: 0 }, { padding: '16px' }], {
103
- duration: 300,
104
- easing: 'cubic-bezier(0.25, 0.8, 0.25, 1)',
105
- });
128
+ /**
129
+ * Waits until the image is decoded, so its natural size is known. Escape cancels the open
130
+ * meanwhile, as it closes the opened image. A failed decode (a broken or not yet available
131
+ * image) leaves the sizes that are known.
132
+ * @param thumbnail - The image on the page.
133
+ */
134
+ async decode(thumbnail) {
135
+ const listeners = new AbortController();
136
+ this.window?.document.addEventListener('keydown', (event) => {
137
+ if (event.key === 'Escape') {
138
+ this.attempt++;
139
+ }
140
+ }, { capture: true, signal: listeners.signal });
141
+ try {
142
+ await thumbnail?.decode?.().catch(() => undefined);
143
+ }
144
+ finally {
145
+ listeners.abort();
146
+ }
147
+ }
148
+ /**
149
+ * The size to scale: the natural size of the image, or the size of the thumbnail for an image
150
+ * without one (an SVG without dimensions), which may then grow to fill the window.
151
+ * @param thumbnail - The image on the page.
152
+ */
153
+ sourceSize(thumbnail) {
154
+ const naturalWidth = thumbnail?.naturalWidth ?? 0;
155
+ const naturalHeight = thumbnail?.naturalHeight ?? 0;
156
+ if (naturalWidth > 0 && naturalHeight > 0) {
157
+ return { width: naturalWidth, height: naturalHeight, natural: true };
158
+ }
159
+ const rect = (thumbnail ?? this.element).getBoundingClientRect();
160
+ return { width: rect.width, height: rect.height, natural: false };
161
+ }
162
+ /**
163
+ * The size the image opens at: its natural size, scaled down to fit the viewport with a margin
164
+ * and never scaled up. An image without a natural size fills the available space with the
165
+ * proportions of its thumbnail.
166
+ * @param source - The size to scale.
167
+ */
168
+ fit(source) {
169
+ const viewport = this.window?.document.documentElement;
170
+ const viewportWidth = viewport?.clientWidth || this.window?.innerWidth || 0;
171
+ const viewportHeight = viewport?.clientHeight || this.window?.innerHeight || 0;
172
+ const margin = viewportWidth <= 640 ? 16 : 48;
173
+ const maxWidth = Math.max(viewportWidth - margin * 2, 1);
174
+ const maxHeight = Math.max(viewportHeight - margin * 2, 1);
175
+ const width = source.width || maxWidth;
176
+ const height = source.height || maxHeight;
177
+ const scale = Math.min(source.natural ? 1 : Number.POSITIVE_INFINITY, maxWidth / width, maxHeight / height);
178
+ return { width: Math.round(width * scale), height: Math.round(height * scale) };
179
+ }
180
+ /**
181
+ * The keyframe that puts the opened image over its thumbnail. The overlay centres the image in
182
+ * the viewport; the frame scales it uniformly to fit the thumbnail's box and centres it there,
183
+ * so an image whose proportions differ from its box's is not distorted.
184
+ * @param thumbnail - The image on the page.
185
+ * @param size - The size of the opened image.
186
+ */
187
+ thumbnailFrame(thumbnail, size) {
188
+ const from = (thumbnail ?? this.element).getBoundingClientRect();
189
+ const viewport = this.window?.document.documentElement;
190
+ const left = ((viewport?.clientWidth ?? size.width) - size.width) / 2;
191
+ const top = ((viewport?.clientHeight ?? size.height) - size.height) / 2;
192
+ const scale = Math.min(from.width / size.width, from.height / size.height) || 0;
193
+ const x = from.left + (from.width - size.width * scale) / 2 - left;
194
+ const y = from.top + (from.height - size.height * scale) / 2 - top;
195
+ return { transformOrigin: '0 0', transform: `translate(${x}px, ${y}px) scale(${scale})` };
106
196
  }
107
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocImageViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
108
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.6", type: NgDocImageViewerComponent, isStandalone: true, selector: "ng-doc-image-viewer", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: true, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "click": "clickEvent()" }, properties: { "attr.data-opened": "!!overlayRef" } }, viewQueries: [{ propertyName: "image", first: true, predicate: ["image"], descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "<ng-content></ng-content>\n\n<ng-template #image>\n <div\n class=\"ng-doc-image-container\"\n (click)=\"overlayRef?.close()\"\n (animate.enter)=\"enterContainer(element)\"\n #element>\n <img class=\"ng-doc-scaled-image\" [src]=\"src()\" [alt]=\"alt()\" />\n </div>\n</ng-template>\n", styles: [":host{display:inline-block;cursor:zoom-in;transition:var(--ng-doc-transition)}:host[data-opened=true]>::ng-deep img{opacity:0}:host>::ng-deep img{max-width:100%;vertical-align:middle;border-radius:var(--ng-doc-base-gutter);overflow:hidden}.ng-doc-image-container{display:flex;align-items:center;justify-content:center;padding:calc(var(--ng-doc-base-gutter) * 2);width:100%;height:100%}.ng-doc-image-container .ng-doc-scaled-image{cursor:zoom-out;width:100%;height:100%}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
197
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocImageViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
198
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.2.1", type: NgDocImageViewerComponent, isStandalone: true, selector: "ng-doc-image-viewer", inputs: { src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: true, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: true, transformFunction: null } }, host: { listeners: { "click": "clickEvent()", "keydown.enter": "clickEvent()", "keydown.space": "onSpace($event)" }, properties: { "attr.role": "isInteractive() ? \"button\" : null", "attr.tabindex": "isInteractive() ? 0 : null", "attr.aria-label": "isInteractive() ? \"Open image: \" + alt() : null", "attr.data-interactive": "isInteractive()", "attr.data-opened": "opened()" } }, viewQueries: [{ propertyName: "image", first: true, predicate: ["image"], descendants: true, read: TemplateRef, isSignal: true }], ngImport: i0, template: "<ng-content></ng-content>\n\n<ng-template #image>\n <div\n class=\"ng-doc-image-container\"\n [style.width.px]=\"size().width\"\n [style.height.px]=\"size().height\"\n (click)=\"overlayRef?.close()\">\n <img class=\"ng-doc-scaled-image\" [src]=\"src()\" [alt]=\"alt()\" />\n </div>\n</ng-template>\n", styles: [":host{display:inline-block}:host[data-interactive=true]{cursor:zoom-in}:host{transition:var(--ng-doc-transition);border-radius:var(--ng-doc-radius-lg)}:host:focus-visible{outline:none;box-shadow:var(--ng-doc-focus-ring)}:host[data-opened=true]>::ng-deep img{opacity:0}:host>::ng-deep img{max-width:100%;vertical-align:middle;border-radius:var(--ng-doc-radius-lg);overflow:hidden}.ng-doc-image-container{display:block}.ng-doc-image-container .ng-doc-scaled-image{display:block;width:100%;height:100%;object-fit:contain;border-radius:var(--ng-doc-radius-lg);cursor:zoom-out}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
109
199
  }
110
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocImageViewerComponent, decorators: [{
200
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocImageViewerComponent, decorators: [{
111
201
  type: Component,
112
202
  args: [{ selector: 'ng-doc-image-viewer', imports: [], changeDetection: ChangeDetectionStrategy.OnPush, host: {
113
- '[attr.data-opened]': '!!overlayRef',
114
- }, template: "<ng-content></ng-content>\n\n<ng-template #image>\n <div\n class=\"ng-doc-image-container\"\n (click)=\"overlayRef?.close()\"\n (animate.enter)=\"enterContainer(element)\"\n #element>\n <img class=\"ng-doc-scaled-image\" [src]=\"src()\" [alt]=\"alt()\" />\n </div>\n</ng-template>\n", styles: [":host{display:inline-block;cursor:zoom-in;transition:var(--ng-doc-transition)}:host[data-opened=true]>::ng-deep img{opacity:0}:host>::ng-deep img{max-width:100%;vertical-align:middle;border-radius:var(--ng-doc-base-gutter);overflow:hidden}.ng-doc-image-container{display:flex;align-items:center;justify-content:center;padding:calc(var(--ng-doc-base-gutter) * 2);width:100%;height:100%}.ng-doc-image-container .ng-doc-scaled-image{cursor:zoom-out;width:100%;height:100%}\n"] }]
115
- }], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: true }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: true }] }], image: [{ type: i0.ViewChild, args: ['image', { ...{ read: TemplateRef }, isSignal: true }] }], clickEvent: [{
116
- type: HostListener,
117
- args: ['click']
118
- }] } });
203
+ '[attr.role]': 'isInteractive() ? "button" : null',
204
+ '[attr.tabindex]': 'isInteractive() ? 0 : null',
205
+ '[attr.aria-label]': 'isInteractive() ? "Open image: " + alt() : null',
206
+ '[attr.data-interactive]': 'isInteractive()',
207
+ '[attr.data-opened]': 'opened()',
208
+ '(click)': 'clickEvent()',
209
+ '(keydown.enter)': 'clickEvent()',
210
+ '(keydown.space)': 'onSpace($event)',
211
+ }, template: "<ng-content></ng-content>\n\n<ng-template #image>\n <div\n class=\"ng-doc-image-container\"\n [style.width.px]=\"size().width\"\n [style.height.px]=\"size().height\"\n (click)=\"overlayRef?.close()\">\n <img class=\"ng-doc-scaled-image\" [src]=\"src()\" [alt]=\"alt()\" />\n </div>\n</ng-template>\n", styles: [":host{display:inline-block}:host[data-interactive=true]{cursor:zoom-in}:host{transition:var(--ng-doc-transition);border-radius:var(--ng-doc-radius-lg)}:host:focus-visible{outline:none;box-shadow:var(--ng-doc-focus-ring)}:host[data-opened=true]>::ng-deep img{opacity:0}:host>::ng-deep img{max-width:100%;vertical-align:middle;border-radius:var(--ng-doc-radius-lg);overflow:hidden}.ng-doc-image-container{display:block}.ng-doc-image-container .ng-doc-scaled-image{display:block;width:100%;height:100%;object-fit:contain;border-radius:var(--ng-doc-radius-lg);cursor:zoom-out}\n"] }]
212
+ }], ctorParameters: () => [], propDecorators: { src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: true }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: true }] }], image: [{ type: i0.ViewChild, args: ['image', { ...{ read: TemplateRef }, isSignal: true }] }] } });
119
213
 
120
214
  /**
121
215
  * Generated bundle index. Do not edit.