@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
@@ -0,0 +1,214 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, DOCUMENT, Injector, NgZone, PLATFORM_ID, signal, computed, afterNextRender, DestroyRef, untracked, Service } from '@angular/core';
3
+ import { isPlatformBrowser } from '@angular/common';
4
+ import { NgDocStoreService } from '@ng-doc/app/services/store';
5
+ import { NgDocThemeService } from '@ng-doc/app/services/theme';
6
+
7
+ /**
8
+ * Whether single-key shortcuts are on for readers who have not turned them on or off themselves.
9
+ * The default is true. Set it with the shortcuts option of provideNgDocApp.
10
+ */
11
+ const NG_DOC_SHORTCUTS = new InjectionToken('NG_DOC_SHORTCUTS', { factory: () => true });
12
+ /**
13
+ * The local storage key that keeps a reader's choice to turn single-key shortcuts on ("1") or off
14
+ * ("0").
15
+ */
16
+ const NG_DOC_STORE_SHORTCUTS_KEY = 'ng-doc-shortcuts';
17
+
18
+ /**
19
+ * Elements where a single key types text or drives the widget instead of running a shortcut:
20
+ * fields, widgets with their own key handling or typeahead, and demos and playgrounds, which
21
+ * host the reader's components.
22
+ */
23
+ const OWN_KEYS = [
24
+ 'input',
25
+ 'textarea',
26
+ 'select',
27
+ '[contenteditable]:not([contenteditable="false"])',
28
+ ...[
29
+ 'combobox',
30
+ 'listbox',
31
+ 'menu',
32
+ 'menubar',
33
+ 'grid',
34
+ 'tree',
35
+ 'treegrid',
36
+ 'slider',
37
+ 'spinbutton',
38
+ 'application',
39
+ ].map((role) => `[role="${role}"]`),
40
+ 'ng-doc-demo',
41
+ 'ng-doc-demo-pane',
42
+ 'ng-doc-playground',
43
+ ].join(', ');
44
+ /**
45
+ * Keyboard shortcuts of the documentation.
46
+ *
47
+ * Single-key shortcuts (slash opens the search, the square brackets open the previous and next
48
+ * page, F focuses the filter of the page, L copies the link to the page and T switches between
49
+ * the light and the dark theme) are on by default. Readers turn them all off with one switch, and
50
+ * the choice is saved in the browser. A site turns them off by default with the shortcuts option
51
+ * of provideNgDocApp. They never run while the reader types in a field or holds Command, Control
52
+ * or Alt. Chords such as Command+K (Control+K) always work.
53
+ */
54
+ class NgDocShortcutsService {
55
+ constructor() {
56
+ this.document = inject(DOCUMENT);
57
+ this.injector = inject(Injector);
58
+ this.ngZone = inject(NgZone);
59
+ this.browser = isPlatformBrowser(inject(PLATFORM_ID));
60
+ this.appDefault = inject(NG_DOC_SHORTCUTS);
61
+ this.userChoice = signal(undefined, /* @ts-ignore */
62
+ ...(ngDevMode ? [{ debugName: "userChoice" }] : /* istanbul ignore next */ []));
63
+ this.shortcuts = [];
64
+ /**
65
+ * Whether single-key shortcuts are on: the reader's saved choice, or the site's default.
66
+ * Components hide the hints of single-key shortcuts while it is false.
67
+ */
68
+ this.enabled = computed(() => this.userChoice() ?? this.appDefault, /* @ts-ignore */
69
+ ...(ngDevMode ? [{ debugName: "enabled" }] : /* istanbul ignore next */ []));
70
+ this.registerDefaults();
71
+ if (!this.browser) {
72
+ return;
73
+ }
74
+ // The saved choice is read after the first render, so a hydrated page first renders exactly
75
+ // what the server rendered (the site's default) and then switches.
76
+ afterNextRender(() => this.userChoice.set(this.readChoice()), { injector: this.injector });
77
+ const listener = (event) => this.onKeydown(event);
78
+ // One document listener for every shortcut; change detection runs only when one matches.
79
+ this.ngZone.runOutsideAngular(() => this.document.addEventListener('keydown', listener));
80
+ inject(DestroyRef).onDestroy(() => this.document.removeEventListener('keydown', listener));
81
+ }
82
+ /**
83
+ * Turns single-key shortcuts on or off for this reader and saves the choice in the browser.
84
+ * @param enabled - Whether single-key shortcuts should run.
85
+ */
86
+ setEnabled(enabled) {
87
+ this.userChoice.set(enabled);
88
+ if (this.browser) {
89
+ try {
90
+ this.injector.get(NgDocStoreService).set(NG_DOC_STORE_SHORTCUTS_KEY, enabled ? '1' : '0');
91
+ }
92
+ catch {
93
+ // Storage can be unavailable (private mode, blocked cookies); the choice lasts the visit.
94
+ }
95
+ }
96
+ }
97
+ /** Turns single-key shortcuts off when they are on, and on when they are off. */
98
+ toggle() {
99
+ this.setEnabled(!untracked(this.enabled));
100
+ }
101
+ /**
102
+ * Registers a shortcut. When several shortcuts use the same key, the one registered last runs,
103
+ * so a page can take over a key while it is shown and give it back when it is destroyed.
104
+ * @param shortcut - The shortcut to register.
105
+ * @returns A function that removes the shortcut.
106
+ */
107
+ register(shortcut) {
108
+ this.shortcuts.push(shortcut);
109
+ return () => {
110
+ const index = this.shortcuts.lastIndexOf(shortcut);
111
+ if (index !== -1) {
112
+ this.shortcuts.splice(index, 1);
113
+ }
114
+ };
115
+ }
116
+ /**
117
+ * Runs the single-key shortcut registered for a key, as if the reader pressed it, even while
118
+ * single-key shortcuts are off. The search palette uses it for its actions.
119
+ * @param key - The key of the shortcut, for example 't'.
120
+ * @returns Whether a shortcut is registered for the key.
121
+ */
122
+ run(key) {
123
+ const shortcut = this.find(key.toLowerCase(), false);
124
+ untracked(() => shortcut?.handler());
125
+ return !!shortcut;
126
+ }
127
+ onKeydown(event) {
128
+ if (event.defaultPrevented || event.isComposing || event.repeat || !event.key) {
129
+ return;
130
+ }
131
+ // AltGr (Control+Alt on Windows) types characters such as [ and ] on many layouts: those
132
+ // keys are plain characters, not chords.
133
+ const altGraph = !!event.getModifierState?.('AltGraph');
134
+ const chord = !altGraph && (event.metaKey || event.ctrlKey) && !event.altKey;
135
+ const shortcut = this.find(event.key.toLowerCase(), chord);
136
+ if (!shortcut || (!chord && !this.acceptsSingleKey(event, altGraph))) {
137
+ return;
138
+ }
139
+ event.preventDefault();
140
+ this.ngZone.run(() => untracked(() => shortcut.handler(event)));
141
+ }
142
+ find(key, chord) {
143
+ for (let index = this.shortcuts.length - 1; index >= 0; index--) {
144
+ const shortcut = this.shortcuts[index];
145
+ if (shortcut.key.toLowerCase() === key && !!shortcut.chord === chord) {
146
+ return shortcut;
147
+ }
148
+ }
149
+ return undefined;
150
+ }
151
+ acceptsSingleKey(event, altGraph) {
152
+ if (!untracked(this.enabled) ||
153
+ event.metaKey ||
154
+ (!altGraph && (event.ctrlKey || event.altKey))) {
155
+ return false;
156
+ }
157
+ const target = event.composedPath?.()[0] ?? event.target;
158
+ return !(target instanceof Element && target.closest(OWN_KEYS));
159
+ }
160
+ readChoice() {
161
+ try {
162
+ const saved = this.injector.get(NgDocStoreService).get(NG_DOC_STORE_SHORTCUTS_KEY);
163
+ return saved === '1' ? true : saved === '0' ? false : undefined;
164
+ }
165
+ catch {
166
+ return undefined;
167
+ }
168
+ }
169
+ registerDefaults() {
170
+ this.register({ key: 't', handler: () => this.toggleDarkTheme() });
171
+ this.register({ key: 'l', handler: () => this.copyLink() });
172
+ // Pager links: rel="prev"/"next" is the standard marker; the classes are the NgDoc pager's.
173
+ this.register({ key: '[', handler: () => this.click('a[rel~="prev"], a.ng-doc-prev-page') });
174
+ this.register({ key: ']', handler: () => this.click('a[rel~="next"], a.ng-doc-next-page') });
175
+ }
176
+ toggleDarkTheme() {
177
+ const themeService = this.injector.get(NgDocThemeService);
178
+ const theme = themeService.theme();
179
+ const toggle = this.themeToggle;
180
+ // A second T restores the theme the first one replaced (auto or a custom theme), unless the
181
+ // reader changed the theme in between.
182
+ if (toggle && toggle.set === theme) {
183
+ this.themeToggle = undefined;
184
+ themeService.set(toggle.previous ?? undefined);
185
+ return;
186
+ }
187
+ const dark = theme === 'dark' ||
188
+ (theme === 'auto' &&
189
+ !!this.document.defaultView?.matchMedia?.('(prefers-color-scheme: dark)').matches);
190
+ const next = dark ? null : 'dark';
191
+ this.themeToggle = { previous: theme, set: next };
192
+ themeService.set(next ?? undefined);
193
+ }
194
+ copyLink() {
195
+ void this.document.defaultView?.navigator.clipboard
196
+ ?.writeText(this.document.location.href)
197
+ .catch(() => undefined);
198
+ }
199
+ click(selector) {
200
+ this.document.querySelector(selector)?.click();
201
+ }
202
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocShortcutsService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
203
+ static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.1", ngImport: i0, type: NgDocShortcutsService }); }
204
+ }
205
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocShortcutsService, decorators: [{
206
+ type: Service
207
+ }], ctorParameters: () => [] });
208
+
209
+ /**
210
+ * Generated bundle index. Do not edit.
211
+ */
212
+
213
+ export { NG_DOC_SHORTCUTS, NG_DOC_STORE_SHORTCUTS_KEY, NgDocShortcutsService };
214
+ //# sourceMappingURL=ng-doc-app-services-shortcuts.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ng-doc-app-services-shortcuts.mjs","sources":["../../../../libs/app/services/shortcuts/shortcut.ts","../../../../libs/app/services/shortcuts/shortcuts.service.ts","../../../../libs/app/services/shortcuts/ng-doc-app-services-shortcuts.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\n\n/**\n * A keyboard shortcut registered with the NgDocShortcutsService.\n */\nexport interface NgDocShortcut {\n /**\n * The key that runs the shortcut, compared with the key of the keyboard event without regard to\n * case, for example '/', '[' or 'f'.\n */\n key: string;\n /**\n * Whether the shortcut is a chord with the Command key (macOS) or the Control key (elsewhere).\n * Chords always work: in text fields too, and when single-key shortcuts are turned off. Other\n * shortcuts are single keys: they run only while single-key shortcuts are on, never while the\n * reader types in a field, and never with Command, Control or Alt held.\n */\n chord?: boolean;\n /**\n * Runs the shortcut. The default action of the keyboard event is prevented before it runs. The\n * event is missing when the shortcut runs from the search palette.\n */\n handler: (event?: KeyboardEvent) => void;\n}\n\n/**\n * Whether single-key shortcuts are on for readers who have not turned them on or off themselves.\n * The default is true. Set it with the shortcuts option of provideNgDocApp.\n */\nexport const NG_DOC_SHORTCUTS: InjectionToken<boolean> = new InjectionToken<boolean>(\n 'NG_DOC_SHORTCUTS',\n { factory: () => true },\n);\n\n/**\n * The local storage key that keeps a reader's choice to turn single-key shortcuts on (\"1\") or off\n * (\"0\").\n */\nexport const NG_DOC_STORE_SHORTCUTS_KEY: string = 'ng-doc-shortcuts';\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n afterNextRender,\n computed,\n DestroyRef,\n DOCUMENT,\n inject,\n Injector,\n NgZone,\n PLATFORM_ID,\n Service,\n Signal,\n signal,\n untracked,\n} from '@angular/core';\nimport { NgDocStoreService } from '@ng-doc/app/services/store';\nimport { NgDocThemeService } from '@ng-doc/app/services/theme';\n\nimport { NG_DOC_SHORTCUTS, NG_DOC_STORE_SHORTCUTS_KEY, NgDocShortcut } from './shortcut';\n\n/**\n * Elements where a single key types text or drives the widget instead of running a shortcut:\n * fields, widgets with their own key handling or typeahead, and demos and playgrounds, which\n * host the reader's components.\n */\nconst OWN_KEYS = [\n 'input',\n 'textarea',\n 'select',\n '[contenteditable]:not([contenteditable=\"false\"])',\n ...[\n 'combobox',\n 'listbox',\n 'menu',\n 'menubar',\n 'grid',\n 'tree',\n 'treegrid',\n 'slider',\n 'spinbutton',\n 'application',\n ].map((role: string) => `[role=\"${role}\"]`),\n 'ng-doc-demo',\n 'ng-doc-demo-pane',\n 'ng-doc-playground',\n].join(', ');\n\n/**\n * Keyboard shortcuts of the documentation.\n *\n * Single-key shortcuts (slash opens the search, the square brackets open the previous and next\n * page, F focuses the filter of the page, L copies the link to the page and T switches between\n * the light and the dark theme) are on by default. Readers turn them all off with one switch, and\n * the choice is saved in the browser. A site turns them off by default with the shortcuts option\n * of provideNgDocApp. They never run while the reader types in a field or holds Command, Control\n * or Alt. Chords such as Command+K (Control+K) always work.\n */\n@Service()\nexport class NgDocShortcutsService {\n private readonly document = inject(DOCUMENT);\n private readonly injector = inject(Injector);\n private readonly ngZone = inject(NgZone);\n private readonly browser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly appDefault = inject(NG_DOC_SHORTCUTS);\n private readonly userChoice = signal<boolean | undefined>(undefined);\n private readonly shortcuts: NgDocShortcut[] = [];\n // The theme T switched away from, and the theme it set, so a second T restores the first.\n private themeToggle?: { previous: string | null; set: string | null };\n\n /**\n * Whether single-key shortcuts are on: the reader's saved choice, or the site's default.\n * Components hide the hints of single-key shortcuts while it is false.\n */\n readonly enabled: Signal<boolean> = computed(() => this.userChoice() ?? this.appDefault);\n\n constructor() {\n this.registerDefaults();\n\n if (!this.browser) {\n return;\n }\n\n // The saved choice is read after the first render, so a hydrated page first renders exactly\n // what the server rendered (the site's default) and then switches.\n afterNextRender(() => this.userChoice.set(this.readChoice()), { injector: this.injector });\n\n const listener = (event: KeyboardEvent) => this.onKeydown(event);\n // One document listener for every shortcut; change detection runs only when one matches.\n this.ngZone.runOutsideAngular(() => this.document.addEventListener('keydown', listener));\n inject(DestroyRef).onDestroy(() => this.document.removeEventListener('keydown', listener));\n }\n\n /**\n * Turns single-key shortcuts on or off for this reader and saves the choice in the browser.\n * @param enabled - Whether single-key shortcuts should run.\n */\n setEnabled(enabled: boolean): void {\n this.userChoice.set(enabled);\n\n if (this.browser) {\n try {\n this.injector.get(NgDocStoreService).set(NG_DOC_STORE_SHORTCUTS_KEY, enabled ? '1' : '0');\n } catch {\n // Storage can be unavailable (private mode, blocked cookies); the choice lasts the visit.\n }\n }\n }\n\n /** Turns single-key shortcuts off when they are on, and on when they are off. */\n toggle(): void {\n this.setEnabled(!untracked(this.enabled));\n }\n\n /**\n * Registers a shortcut. When several shortcuts use the same key, the one registered last runs,\n * so a page can take over a key while it is shown and give it back when it is destroyed.\n * @param shortcut - The shortcut to register.\n * @returns A function that removes the shortcut.\n */\n register(shortcut: NgDocShortcut): () => void {\n this.shortcuts.push(shortcut);\n\n return () => {\n const index = this.shortcuts.lastIndexOf(shortcut);\n\n if (index !== -1) {\n this.shortcuts.splice(index, 1);\n }\n };\n }\n\n /**\n * Runs the single-key shortcut registered for a key, as if the reader pressed it, even while\n * single-key shortcuts are off. The search palette uses it for its actions.\n * @param key - The key of the shortcut, for example 't'.\n * @returns Whether a shortcut is registered for the key.\n */\n run(key: string): boolean {\n const shortcut = this.find(key.toLowerCase(), false);\n\n untracked(() => shortcut?.handler());\n\n return !!shortcut;\n }\n\n private onKeydown(event: KeyboardEvent): void {\n if (event.defaultPrevented || event.isComposing || event.repeat || !event.key) {\n return;\n }\n\n // AltGr (Control+Alt on Windows) types characters such as [ and ] on many layouts: those\n // keys are plain characters, not chords.\n const altGraph = !!event.getModifierState?.('AltGraph');\n const chord = !altGraph && (event.metaKey || event.ctrlKey) && !event.altKey;\n const shortcut = this.find(event.key.toLowerCase(), chord);\n\n if (!shortcut || (!chord && !this.acceptsSingleKey(event, altGraph))) {\n return;\n }\n\n event.preventDefault();\n this.ngZone.run(() => untracked(() => shortcut.handler(event)));\n }\n\n private find(key: string, chord: boolean): NgDocShortcut | undefined {\n for (let index = this.shortcuts.length - 1; index >= 0; index--) {\n const shortcut = this.shortcuts[index];\n\n if (shortcut.key.toLowerCase() === key && !!shortcut.chord === chord) {\n return shortcut;\n }\n }\n\n return undefined;\n }\n\n private acceptsSingleKey(event: KeyboardEvent, altGraph: boolean): boolean {\n if (\n !untracked(this.enabled) ||\n event.metaKey ||\n (!altGraph && (event.ctrlKey || event.altKey))\n ) {\n return false;\n }\n\n const target = event.composedPath?.()[0] ?? event.target;\n\n return !(target instanceof Element && target.closest(OWN_KEYS));\n }\n\n private readChoice(): boolean | undefined {\n try {\n const saved = this.injector.get(NgDocStoreService).get(NG_DOC_STORE_SHORTCUTS_KEY);\n\n return saved === '1' ? true : saved === '0' ? false : undefined;\n } catch {\n return undefined;\n }\n }\n\n private registerDefaults(): void {\n this.register({ key: 't', handler: () => this.toggleDarkTheme() });\n this.register({ key: 'l', handler: () => this.copyLink() });\n // Pager links: rel=\"prev\"/\"next\" is the standard marker; the classes are the NgDoc pager's.\n this.register({ key: '[', handler: () => this.click('a[rel~=\"prev\"], a.ng-doc-prev-page') });\n this.register({ key: ']', handler: () => this.click('a[rel~=\"next\"], a.ng-doc-next-page') });\n }\n\n private toggleDarkTheme(): void {\n const themeService = this.injector.get(NgDocThemeService);\n const theme = themeService.theme();\n const toggle = this.themeToggle;\n\n // A second T restores the theme the first one replaced (auto or a custom theme), unless the\n // reader changed the theme in between.\n if (toggle && toggle.set === theme) {\n this.themeToggle = undefined;\n themeService.set(toggle.previous ?? undefined);\n\n return;\n }\n\n const dark =\n theme === 'dark' ||\n (theme === 'auto' &&\n !!this.document.defaultView?.matchMedia?.('(prefers-color-scheme: dark)').matches);\n const next = dark ? null : 'dark';\n\n this.themeToggle = { previous: theme, set: next };\n themeService.set(next ?? undefined);\n }\n\n private copyLink(): void {\n void this.document.defaultView?.navigator.clipboard\n ?.writeText(this.document.location.href)\n .catch(() => undefined);\n }\n\n private click(selector: string): void {\n this.document.querySelector<HTMLElement>(selector)?.click();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,42 +1,67 @@
1
+ import { isPlatformBrowser } from '@angular/common';
1
2
  import * as i0 from '@angular/core';
2
- import { inject, DOCUMENT, Injectable } from '@angular/core';
3
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
3
+ import { inject, DOCUMENT, NgZone, PLATFORM_ID, DestroyRef, Service } from '@angular/core';
4
+ import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
5
  import { Router, NavigationEnd } from '@angular/router';
5
- import { ngDocZoneOptimize } from '@ng-doc/ui-kit';
6
6
  import { NgDocScrollService } from '@ng-doc/ui-kit/services/scroll';
7
7
  import { WA_WINDOW } from '@ng-web-apis/common';
8
- import { BehaviorSubject, fromEvent, share } from 'rxjs';
9
- import { startWith, filter } from 'rxjs/operators';
8
+ import { BehaviorSubject } from 'rxjs';
9
+ import { filter } from 'rxjs/operators';
10
10
 
11
+ /**
12
+ * The widest viewport, in CSS pixels, at which the sidebar is an overlay that the navbar's menu
13
+ * button opens. It matches the `max-width: 900px` media query of `ng-doc-sidenav`.
14
+ */
15
+ const NG_DOC_SIDEBAR_COLLAPSE_WIDTH = 900;
11
16
  /**
12
17
  * Service for sidebar, it can be used to hide/show sidebar or to check if sidebar is collapsable.
18
+ *
19
+ * On wide screens the sidebar is always shown. At `NG_DOC_SIDEBAR_COLLAPSE_WIDTH` and below it is
20
+ * an overlay: `show()` opens it and blocks page scrolling, `hide()` closes it, and a navigation
21
+ * or a resize to a wide screen ends the overlay.
13
22
  */
14
23
  class NgDocSidebarService {
15
24
  constructor() {
16
- this.expanded = new BehaviorSubject(false);
17
25
  this.document = inject(DOCUMENT);
18
26
  this.window = inject(WA_WINDOW);
19
27
  this.router = inject(Router);
20
28
  this.scroll = inject(NgDocScrollService);
21
- const windowResize = fromEvent(this.window, 'resize').pipe(takeUntilDestroyed(), startWith(null), share());
22
- windowResize
23
- .pipe(filter(() => this.expanded.value && this.isMobile), ngDocZoneOptimize(), takeUntilDestroyed())
24
- .subscribe(() => this.hide());
25
- windowResize
26
- .pipe(filter(() => !this.expanded.value && !this.isMobile), ngDocZoneOptimize(), takeUntilDestroyed())
27
- .subscribe(() => this.show());
28
- windowResize
29
- .pipe(filter(() => this.expanded.value && !this.isMobile), ngDocZoneOptimize(), takeUntilDestroyed())
30
- .subscribe(() => this.scroll.unblock());
29
+ this.expanded = new BehaviorSubject(false);
30
+ /**
31
+ * Whether the sidebar is shown, as a signal. It follows the same state as `isExpanded()`.
32
+ */
33
+ this.expandedState = toSignal(this.expanded, { requireSync: true });
34
+ this.ngZone = inject(NgZone);
35
+ // On the server the sidebar stays closed: the prerendered page must not show the overlay
36
+ // open on narrow screens. The browser opens it on wide screens once the app starts.
37
+ if (isPlatformBrowser(inject(PLATFORM_ID))) {
38
+ this.syncWithViewport();
39
+ const onResize = () => {
40
+ // Only a change of the open state re-enters the Angular zone, so `isExpanded()`
41
+ // subscribers run inside it; other resizes stay outside.
42
+ if (this.isMobile === this.expanded.value) {
43
+ this.ngZone.run(() => this.syncWithViewport());
44
+ }
45
+ else if (this.expanded.value) {
46
+ this.scroll.unblock();
47
+ }
48
+ };
49
+ this.ngZone.runOutsideAngular(() => this.window.addEventListener('resize', onResize));
50
+ inject(DestroyRef).onDestroy(() => this.window.removeEventListener('resize', onResize));
51
+ }
31
52
  this.router.events
32
53
  .pipe(filter((event) => event instanceof NavigationEnd && this.expanded.value && this.isMobile), takeUntilDestroyed())
33
54
  .subscribe(() => this.hide());
34
55
  }
56
+ /**
57
+ * Whether the viewport is narrow enough for the sidebar to be an overlay.
58
+ */
35
59
  get isMobile() {
36
- return this.window.innerWidth < 1024;
60
+ return this.window.innerWidth <= NG_DOC_SIDEBAR_COLLAPSE_WIDTH;
37
61
  }
38
62
  /**
39
63
  * Indicates if sidebar is visible, based on the show/hide methods.
64
+ * Read `expandedState` for the same value as a signal.
40
65
  */
41
66
  isExpanded() {
42
67
  return this.expanded.asObservable();
@@ -65,19 +90,28 @@ class NgDocSidebarService {
65
90
  toggle() {
66
91
  this.expanded.value ? this.hide() : this.show();
67
92
  }
68
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocSidebarService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
69
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocSidebarService, providedIn: 'root' }); }
93
+ syncWithViewport() {
94
+ if (this.isMobile) {
95
+ this.hide();
96
+ }
97
+ else if (this.expanded.value) {
98
+ // The overlay may have blocked scrolling before the viewport grew.
99
+ this.scroll.unblock();
100
+ }
101
+ else {
102
+ this.show();
103
+ }
104
+ }
105
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocSidebarService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
106
+ static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.1", ngImport: i0, type: NgDocSidebarService }); }
70
107
  }
71
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocSidebarService, decorators: [{
72
- type: Injectable,
73
- args: [{
74
- providedIn: 'root',
75
- }]
108
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocSidebarService, decorators: [{
109
+ type: Service
76
110
  }], ctorParameters: () => [] });
77
111
 
78
112
  /**
79
113
  * Generated bundle index. Do not edit.
80
114
  */
81
115
 
82
- export { NgDocSidebarService };
116
+ export { NG_DOC_SIDEBAR_COLLAPSE_WIDTH, NgDocSidebarService };
83
117
  //# sourceMappingURL=ng-doc-app-services-sidebar.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ng-doc-app-services-sidebar.mjs","sources":["../../../../libs/app/services/sidebar/sidebar.service.ts","../../../../libs/app/services/sidebar/ng-doc-app-services-sidebar.ts"],"sourcesContent":["import { DOCUMENT, inject, Injectable } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { NavigationEnd, Router } from '@angular/router';\nimport { ngDocZoneOptimize } from '@ng-doc/ui-kit';\nimport { NgDocScrollService } from '@ng-doc/ui-kit/services/scroll';\nimport { WA_WINDOW } from '@ng-web-apis/common';\nimport { BehaviorSubject, fromEvent, Observable, share } from 'rxjs';\nimport { filter, startWith } from 'rxjs/operators';\n\n/**\n * Service for sidebar, it can be used to hide/show sidebar or to check if sidebar is collapsable.\n */\n@Injectable({\n providedIn: 'root',\n})\nexport class NgDocSidebarService {\n protected readonly expanded = new BehaviorSubject<boolean>(false);\n protected readonly document = inject(DOCUMENT);\n protected readonly window = inject(WA_WINDOW);\n protected readonly router = inject(Router);\n protected readonly scroll = inject(NgDocScrollService);\n\n constructor() {\n const windowResize = fromEvent(this.window, 'resize').pipe(\n takeUntilDestroyed(),\n startWith(null),\n share(),\n );\n\n windowResize\n .pipe(\n filter(() => this.expanded.value && this.isMobile),\n ngDocZoneOptimize(),\n takeUntilDestroyed(),\n )\n .subscribe(() => this.hide());\n\n windowResize\n .pipe(\n filter(() => !this.expanded.value && !this.isMobile),\n ngDocZoneOptimize(),\n takeUntilDestroyed(),\n )\n .subscribe(() => this.show());\n\n windowResize\n .pipe(\n filter(() => this.expanded.value && !this.isMobile),\n ngDocZoneOptimize(),\n takeUntilDestroyed(),\n )\n .subscribe(() => this.scroll.unblock());\n\n this.router.events\n .pipe(\n filter((event) => event instanceof NavigationEnd && this.expanded.value && this.isMobile),\n takeUntilDestroyed(),\n )\n .subscribe(() => this.hide());\n }\n\n get isMobile(): boolean {\n return this.window.innerWidth < 1024;\n }\n\n /**\n * Indicates if sidebar is visible, based on the show/hide methods.\n */\n isExpanded(): Observable<boolean> {\n return this.expanded.asObservable();\n }\n\n /**\n * Show sidebar, and block scrolling.\n */\n show(): void {\n if (!this.expanded.value) {\n this.expanded.next(true);\n this.isMobile && this.scroll.block();\n }\n }\n\n /**\n * Hide sidebar, and unblock scrolling.\n */\n hide(): void {\n if (this.expanded.value) {\n this.expanded.next(false);\n this.isMobile && this.scroll.unblock();\n }\n }\n\n /**\n * Toggle sidebar visibility.\n */\n toggle(): void {\n this.expanded.value ? this.hide() : this.show();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ng-doc-app-services-sidebar.mjs","sources":["../../../../libs/app/services/sidebar/sidebar.service.ts","../../../../libs/app/services/sidebar/ng-doc-app-services-sidebar.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport { DestroyRef, DOCUMENT, inject, NgZone, PLATFORM_ID, Service, Signal } from '@angular/core';\nimport { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';\nimport { NavigationEnd, Router } from '@angular/router';\nimport { NgDocScrollService } from '@ng-doc/ui-kit/services/scroll';\nimport { WA_WINDOW } from '@ng-web-apis/common';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport { filter } from 'rxjs/operators';\n\n/**\n * The widest viewport, in CSS pixels, at which the sidebar is an overlay that the navbar's menu\n * button opens. It matches the `max-width: 900px` media query of `ng-doc-sidenav`.\n */\nexport const NG_DOC_SIDEBAR_COLLAPSE_WIDTH = 900;\n\n/**\n * Service for sidebar, it can be used to hide/show sidebar or to check if sidebar is collapsable.\n *\n * On wide screens the sidebar is always shown. At `NG_DOC_SIDEBAR_COLLAPSE_WIDTH` and below it is\n * an overlay: `show()` opens it and blocks page scrolling, `hide()` closes it, and a navigation\n * or a resize to a wide screen ends the overlay.\n */\n@Service()\nexport class NgDocSidebarService {\n protected readonly document = inject(DOCUMENT);\n protected readonly window = inject(WA_WINDOW);\n protected readonly router = inject(Router);\n protected readonly scroll = inject(NgDocScrollService);\n\n protected readonly expanded = new BehaviorSubject<boolean>(false);\n\n /**\n * Whether the sidebar is shown, as a signal. It follows the same state as `isExpanded()`.\n */\n readonly expandedState: Signal<boolean> = toSignal(this.expanded, { requireSync: true });\n\n private readonly ngZone = inject(NgZone);\n\n constructor() {\n // On the server the sidebar stays closed: the prerendered page must not show the overlay\n // open on narrow screens. The browser opens it on wide screens once the app starts.\n if (isPlatformBrowser(inject(PLATFORM_ID))) {\n this.syncWithViewport();\n\n const onResize = (): void => {\n // Only a change of the open state re-enters the Angular zone, so `isExpanded()`\n // subscribers run inside it; other resizes stay outside.\n if (this.isMobile === this.expanded.value) {\n this.ngZone.run(() => this.syncWithViewport());\n } else if (this.expanded.value) {\n this.scroll.unblock();\n }\n };\n\n this.ngZone.runOutsideAngular(() => this.window.addEventListener('resize', onResize));\n inject(DestroyRef).onDestroy(() => this.window.removeEventListener('resize', onResize));\n }\n\n this.router.events\n .pipe(\n filter((event) => event instanceof NavigationEnd && this.expanded.value && this.isMobile),\n takeUntilDestroyed(),\n )\n .subscribe(() => this.hide());\n }\n\n /**\n * Whether the viewport is narrow enough for the sidebar to be an overlay.\n */\n get isMobile(): boolean {\n return this.window.innerWidth <= NG_DOC_SIDEBAR_COLLAPSE_WIDTH;\n }\n\n /**\n * Indicates if sidebar is visible, based on the show/hide methods.\n * Read `expandedState` for the same value as a signal.\n */\n isExpanded(): Observable<boolean> {\n return this.expanded.asObservable();\n }\n\n /**\n * Show sidebar, and block scrolling.\n */\n show(): void {\n if (!this.expanded.value) {\n this.expanded.next(true);\n this.isMobile && this.scroll.block();\n }\n }\n\n /**\n * Hide sidebar, and unblock scrolling.\n */\n hide(): void {\n if (this.expanded.value) {\n this.expanded.next(false);\n this.isMobile && this.scroll.unblock();\n }\n }\n\n /**\n * Toggle sidebar visibility.\n */\n toggle(): void {\n this.expanded.value ? this.hide() : this.show();\n }\n\n private syncWithViewport(): void {\n if (this.isMobile) {\n this.hide();\n } else if (this.expanded.value) {\n // The overlay may have blocked scrolling before the viewport grew.\n this.scroll.unblock();\n } else {\n this.show();\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,8 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injectable } from '@angular/core';
2
+ import { inject, Service } from '@angular/core';
3
3
  import { WA_LOCAL_STORAGE } from '@ng-web-apis/common';
4
4
 
5
5
  const DEFAULT_SERIALIZE = (v) => String(v);
6
+ /**
7
+ * Reads and writes NgDoc settings (for example the theme) in the browser's local storage.
8
+ */
6
9
  class NgDocStoreService {
7
10
  constructor() {
8
11
  this.localStorage = inject(WA_LOCAL_STORAGE);
@@ -15,14 +18,11 @@ class NgDocStoreService {
15
18
  ? deserialize(this.localStorage.getItem(key))
16
19
  : this.localStorage.getItem(key);
17
20
  }
18
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocStoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
19
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocStoreService, providedIn: 'root' }); }
21
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocStoreService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
22
+ static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.1", ngImport: i0, type: NgDocStoreService }); }
20
23
  }
21
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocStoreService, decorators: [{
22
- type: Injectable,
23
- args: [{
24
- providedIn: 'root',
25
- }]
24
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocStoreService, decorators: [{
25
+ type: Service
26
26
  }] });
27
27
 
28
28
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ng-doc-app-services-store.mjs","sources":["../../../../libs/app/services/store/store.service.ts","../../../../libs/app/services/store/ng-doc-app-services-store.ts"],"sourcesContent":["import { inject, Injectable } from '@angular/core';\nimport { WA_LOCAL_STORAGE } from '@ng-web-apis/common';\n\nconst DEFAULT_SERIALIZE: (v: unknown) => string = (v: unknown) => String(v);\n\n@Injectable({\n\tprovidedIn: 'root',\n})\nexport class NgDocStoreService {\n\tprotected readonly localStorage: Storage = inject(WA_LOCAL_STORAGE);\n\n\tset(key: string, data: string): void;\n\tset<T>(key: string, data: T, serialize: (v: T) => string): void;\n\tset<T>(key: string, data: T, serialize: (v: T) => string = DEFAULT_SERIALIZE): void {\n\t\treturn this.localStorage.setItem(key, serialize(data));\n\t}\n\n\tget(key: string): string | null;\n\tget<T>(key: string, deserialize: (v: string | null) => T): T;\n\tget<T>(key: string, deserialize?: (v: string | null) => T): T | string | null {\n\t\treturn deserialize\n\t\t\t? deserialize(this.localStorage.getItem(key))\n\t\t\t: this.localStorage.getItem(key);\n\t}\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAGA,MAAM,iBAAiB,GAA2B,CAAC,CAAU,KAAK,MAAM,CAAC,CAAC,CAAC;MAK9D,iBAAiB,CAAA;AAH9B,IAAA,WAAA,GAAA;AAIoB,QAAA,IAAA,CAAA,YAAY,GAAY,MAAM,CAAC,gBAAgB,CAAC;AAenE,IAAA;AAXA,IAAA,GAAG,CAAI,GAAW,EAAE,IAAO,EAAE,YAA8B,iBAAiB,EAAA;AAC3E,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACvD;IAIA,GAAG,CAAI,GAAW,EAAE,WAAqC,EAAA;AACxD,QAAA,OAAO;cACJ,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;cAC1C,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;IAClC;8GAfY,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAAjB,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,iBAAiB,cAFjB,MAAM,EAAA,CAAA,CAAA;;2FAEN,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAH7B,UAAU;AAAC,YAAA,IAAA,EAAA,CAAA;AACX,oBAAA,UAAU,EAAE,MAAM;AAClB,iBAAA;;;ACPD;;AAEG;;;;"}
1
+ {"version":3,"file":"ng-doc-app-services-store.mjs","sources":["../../../../libs/app/services/store/store.service.ts","../../../../libs/app/services/store/ng-doc-app-services-store.ts"],"sourcesContent":["import { inject, Service } from '@angular/core';\nimport { WA_LOCAL_STORAGE } from '@ng-web-apis/common';\n\nconst DEFAULT_SERIALIZE: (v: unknown) => string = (v: unknown) => String(v);\n\n/**\n * Reads and writes NgDoc settings (for example the theme) in the browser's local storage.\n */\n@Service()\nexport class NgDocStoreService {\n protected readonly localStorage: Storage = inject(WA_LOCAL_STORAGE);\n\n /**\n * Stores a string value.\n * @param key - Storage key.\n * @param data - The value to store.\n */\n set(key: string, data: string): void;\n /**\n * Stores a value after serializing it.\n * @param key - Storage key.\n * @param data - The value to store.\n * @param serialize - Converts the value to a string.\n */\n set<T>(key: string, data: T, serialize: (v: T) => string): void;\n set<T>(key: string, data: T, serialize: (v: T) => string = DEFAULT_SERIALIZE): void {\n return this.localStorage.setItem(key, serialize(data));\n }\n\n /**\n * Returns the stored string, or `null` when the key is not set.\n * @param key - Storage key.\n */\n get(key: string): string | null;\n /**\n * Returns the stored value, deserialized.\n * @param key - Storage key.\n * @param deserialize - Converts the stored string (or `null`) to the value.\n */\n get<T>(key: string, deserialize: (v: string | null) => T): T;\n get<T>(key: string, deserialize?: (v: string | null) => T): T | string | null {\n return deserialize\n ? deserialize(this.localStorage.getItem(key))\n : this.localStorage.getItem(key);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAGA,MAAM,iBAAiB,GAA2B,CAAC,CAAU,KAAK,MAAM,CAAC,CAAC,CAAC;AAE3E;;AAEG;MAEU,iBAAiB,CAAA;AAD9B,IAAA,WAAA,GAAA;AAEqB,QAAA,IAAA,CAAA,YAAY,GAAY,MAAM,CAAC,gBAAgB,CAAC;AAmCpE,IAAA;AApBC,IAAA,GAAG,CAAI,GAAW,EAAE,IAAO,EAAE,YAA8B,iBAAiB,EAAA;AAC1E,QAAA,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACxD;IAaA,GAAG,CAAI,GAAW,EAAE,WAAqC,EAAA;AACvD,QAAA,OAAO;cACH,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;cAC1C,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;IACpC;8GAnCW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,OAAA,EAAA,CAAA,CAAA;+GAAjB,iBAAiB,EAAA,CAAA,CAAA;;2FAAjB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAD7B;;;ACRD;;AAEG;;;;"}
@@ -1,11 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, DOCUMENT, Injectable } from '@angular/core';
2
+ import { inject, DOCUMENT, signal, Service } from '@angular/core';
3
3
  import { NG_DOC_STORE_THEME_KEY } from '@ng-doc/app/constants';
4
4
  import { NgDocStoreService } from '@ng-doc/app/services/store';
5
5
  import { Subject } from 'rxjs';
6
6
 
7
7
  /**
8
8
  * Service for managing themes.
9
+ *
10
+ * The theme is the `data-theme` attribute of the document element: `auto`, `dark`, the id of a
11
+ * custom theme, or no attribute for the light theme.
9
12
  */
10
13
  class NgDocThemeService {
11
14
  constructor() {
@@ -13,13 +16,25 @@ class NgDocThemeService {
13
16
  this.store = inject(NgDocStoreService);
14
17
  this.change$ = new Subject();
15
18
  this.documentElement = this.document.documentElement;
19
+ this.themeState = signal(this.readTheme(), /* @ts-ignore */
20
+ ...(ngDevMode ? [{ debugName: "themeState" }] : /* istanbul ignore next */ []));
21
+ /**
22
+ * The current theme as a signal: the theme id, or `null` for the light theme.
23
+ *
24
+ * It starts with the document's `data-theme` attribute and follows every `set()` call.
25
+ */
26
+ this.theme = this.themeState.asReadonly();
16
27
  }
17
28
  /**
18
- * Returns the current theme.
29
+ * Returns the current theme: the document's `data-theme` attribute, or `null` without one.
19
30
  */
20
31
  get currentTheme() {
21
- return document.documentElement.getAttribute('data-theme');
32
+ return this.readTheme();
22
33
  }
34
+ /**
35
+ * Emits the theme after every `set()` call. Read `theme` for the current value as a
36
+ * signal.
37
+ */
23
38
  themeChanges() {
24
39
  return this.change$.asObservable();
25
40
  }
@@ -28,18 +43,23 @@ class NgDocThemeService {
28
43
  * @param id - Theme id. If not provided, the theme will be removed.
29
44
  */
30
45
  set(id) {
31
- id
32
- ? this.documentElement.setAttribute('data-theme', id)
46
+ // An empty id removes the attribute, so it is the light theme (`null`) everywhere.
47
+ const theme = id || null;
48
+ theme
49
+ ? this.documentElement.setAttribute('data-theme', theme)
33
50
  : this.documentElement.removeAttribute('data-theme');
34
- this.store.set(NG_DOC_STORE_THEME_KEY, id ?? '');
35
- this.change$.next(id ?? null);
51
+ this.store.set(NG_DOC_STORE_THEME_KEY, theme ?? '');
52
+ this.themeState.set(theme);
53
+ this.change$.next(theme);
54
+ }
55
+ readTheme() {
56
+ return this.documentElement.getAttribute('data-theme');
36
57
  }
37
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocThemeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
38
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocThemeService, providedIn: 'root' }); }
58
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocThemeService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
59
+ static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.2.1", ngImport: i0, type: NgDocThemeService }); }
39
60
  }
40
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocThemeService, decorators: [{
41
- type: Injectable,
42
- args: [{ providedIn: 'root' }]
61
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocThemeService, decorators: [{
62
+ type: Service
43
63
  }] });
44
64
 
45
65
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ng-doc-app-services-theme.mjs","sources":["../../../../libs/app/services/theme/theme.service.ts","../../../../libs/app/services/theme/ng-doc-app-services-theme.ts"],"sourcesContent":["import { DOCUMENT, inject, Injectable } from '@angular/core';\nimport { NG_DOC_STORE_THEME_KEY } from '@ng-doc/app/constants';\nimport { NgDocStoreService } from '@ng-doc/app/services/store';\nimport { Observable, Subject } from 'rxjs';\n\n/**\n * Service for managing themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class NgDocThemeService {\n protected readonly document = inject(DOCUMENT);\n protected readonly store = inject(NgDocStoreService);\n protected readonly change$ = new Subject<string | null>();\n protected readonly documentElement = this.document.documentElement;\n\n /**\n * Returns the current theme.\n */\n get currentTheme(): string | null {\n return document.documentElement.getAttribute('data-theme');\n }\n\n themeChanges(): Observable<string | null> {\n return this.change$.asObservable();\n }\n\n /**\n * Sets the theme by id.\n * @param id - Theme id. If not provided, the theme will be removed.\n */\n set(id?: string): void {\n id\n ? this.documentElement.setAttribute('data-theme', id)\n : this.documentElement.removeAttribute('data-theme');\n\n this.store.set(NG_DOC_STORE_THEME_KEY, id ?? '');\n this.change$.next(id ?? null);\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;AAKA;;AAEG;MAEU,iBAAiB,CAAA;AAD9B,IAAA,WAAA,GAAA;AAEqB,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AAC3B,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC;AACjC,QAAA,IAAA,CAAA,OAAO,GAAG,IAAI,OAAO,EAAiB;AACtC,QAAA,IAAA,CAAA,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe;AAyBnE,IAAA;AAvBC;;AAEG;AACH,IAAA,IAAI,YAAY,GAAA;QACd,OAAO,QAAQ,CAAC,eAAe,CAAC,YAAY,CAAC,YAAY,CAAC;IAC5D;IAEA,YAAY,GAAA;AACV,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE;IACpC;AAEA;;;AAGG;AACH,IAAA,GAAG,CAAC,EAAW,EAAA;QACb;cACI,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,YAAY,EAAE,EAAE;cAClD,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,YAAY,CAAC;QAEtD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,sBAAsB,EAAE,EAAE,IAAI,EAAE,CAAC;QAChD,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC;IAC/B;8GA5BW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA,CAAA;AAAjB,IAAA,SAAA,IAAA,CAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,iBAAiB,cADJ,MAAM,EAAA,CAAA,CAAA;;2FACnB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAD7B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACRlC;;AAEG;;;;"}
1
+ {"version":3,"file":"ng-doc-app-services-theme.mjs","sources":["../../../../libs/app/services/theme/theme.service.ts","../../../../libs/app/services/theme/ng-doc-app-services-theme.ts"],"sourcesContent":["import { DOCUMENT, inject, Service, Signal, signal } from '@angular/core';\nimport { NG_DOC_STORE_THEME_KEY } from '@ng-doc/app/constants';\nimport { NgDocStoreService } from '@ng-doc/app/services/store';\nimport { Observable, Subject } from 'rxjs';\n\n/**\n * Service for managing themes.\n *\n * The theme is the `data-theme` attribute of the document element: `auto`, `dark`, the id of a\n * custom theme, or no attribute for the light theme.\n */\n@Service()\nexport class NgDocThemeService {\n protected readonly document = inject(DOCUMENT);\n protected readonly store = inject(NgDocStoreService);\n protected readonly change$ = new Subject<string | null>();\n protected readonly documentElement = this.document.documentElement;\n\n private readonly themeState = signal<string | null>(this.readTheme());\n\n /**\n * The current theme as a signal: the theme id, or `null` for the light theme.\n *\n * It starts with the document's `data-theme` attribute and follows every `set()` call.\n */\n readonly theme: Signal<string | null> = this.themeState.asReadonly();\n\n /**\n * Returns the current theme: the document's `data-theme` attribute, or `null` without one.\n */\n get currentTheme(): string | null {\n return this.readTheme();\n }\n\n /**\n * Emits the theme after every `set()` call. Read `theme` for the current value as a\n * signal.\n */\n themeChanges(): Observable<string | null> {\n return this.change$.asObservable();\n }\n\n /**\n * Sets the theme by id.\n * @param id - Theme id. If not provided, the theme will be removed.\n */\n set(id?: string): void {\n // An empty id removes the attribute, so it is the light theme (`null`) everywhere.\n const theme = id || null;\n\n theme\n ? this.documentElement.setAttribute('data-theme', theme)\n : this.documentElement.removeAttribute('data-theme');\n\n this.store.set(NG_DOC_STORE_THEME_KEY, theme ?? '');\n this.themeState.set(theme);\n this.change$.next(theme);\n }\n\n private readTheme(): string | null {\n return this.documentElement.getAttribute('data-theme');\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;AAKA;;;;;AAKG;MAEU,iBAAiB,CAAA;AAD9B,IAAA,WAAA,GAAA;AAEqB,QAAA,IAAA,CAAA,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AAC3B,QAAA,IAAA,CAAA,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC;AACjC,QAAA,IAAA,CAAA,OAAO,GAAG,IAAI,OAAO,EAAiB;AACtC,QAAA,IAAA,CAAA,eAAe,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe;AAEjD,QAAA,IAAA,CAAA,UAAU,GAAG,MAAM,CAAgB,IAAI,CAAC,SAAS,EAAE;uFAAC;AAErE;;;;AAIG;AACM,QAAA,IAAA,CAAA,KAAK,GAA0B,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE;AAqCrE,IAAA;AAnCC;;AAEG;AACH,IAAA,IAAI,YAAY,GAAA;AACd,QAAA,OAAO,IAAI,CAAC,SAAS,EAAE;IACzB;AAEA;;;AAGG;IACH,YAAY,GAAA;AACV,QAAA,OAAO,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE;IACpC;AAEA;;;AAGG;AACH,IAAA,GAAG,CAAC,EAAW,EAAA;;AAEb,QAAA,MAAM,KAAK,GAAG,EAAE,IAAI,IAAI;QAExB;cACI,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK;cACrD,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,YAAY,CAAC;QAEtD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC;AACnD,QAAA,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC;AAC1B,QAAA,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;IAC1B;IAEQ,SAAS,GAAA;QACf,OAAO,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,YAAY,CAAC;IACxD;8GAjDW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,OAAA,EAAA,CAAA,CAAA;+GAAjB,iBAAiB,EAAA,CAAA,CAAA;;2FAAjB,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAD7B;;;ACXD;;AAEG;;;;"}