@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.
- package/README.md +20 -29
- package/fesm2022/ng-doc-app-classes-content-anchor-controller.mjs +363 -0
- package/fesm2022/ng-doc-app-classes-content-anchor-controller.mjs.map +1 -0
- package/fesm2022/ng-doc-app-classes-content-controller.mjs +299 -0
- package/fesm2022/ng-doc-app-classes-content-controller.mjs.map +1 -0
- package/fesm2022/ng-doc-app-classes-default-search-engine.mjs +51 -35
- package/fesm2022/ng-doc-app-classes-default-search-engine.mjs.map +1 -1
- package/fesm2022/ng-doc-app-classes-root-page.mjs.map +1 -1
- package/fesm2022/ng-doc-app-classes-search-engine.mjs.map +1 -1
- package/fesm2022/ng-doc-app-classes.mjs +2 -0
- package/fesm2022/ng-doc-app-classes.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-api-list.mjs +173 -69
- package/fesm2022/ng-doc-app-components-api-list.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-breadcrumb.mjs +13 -10
- package/fesm2022/ng-doc-app-components-breadcrumb.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-code.mjs +152 -25
- package/fesm2022/ng-doc-app-components-code.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-command-palette.mjs +498 -0
- package/fesm2022/ng-doc-app-components-command-palette.mjs.map +1 -0
- package/fesm2022/ng-doc-app-components-copy-button.mjs +38 -21
- package/fesm2022/ng-doc-app-components-copy-button.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-demo-displayer.mjs +36 -29
- package/fesm2022/ng-doc-app-components-demo-displayer.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-demo-frame.mjs +96 -0
- package/fesm2022/ng-doc-app-components-demo-frame.mjs.map +1 -0
- package/fesm2022/ng-doc-app-components-demo-pane.mjs +39 -36
- package/fesm2022/ng-doc-app-components-demo-pane.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-demo.mjs +175 -44
- package/fesm2022/ng-doc-app-components-demo.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-fullscreen-button.mjs +12 -9
- package/fesm2022/ng-doc-app-components-fullscreen-button.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs +79 -0
- package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs.map +1 -0
- package/fesm2022/ng-doc-app-components-heading-anchor.mjs +54 -21
- package/fesm2022/ng-doc-app-components-heading-anchor.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-image-viewer.mjs +183 -89
- package/fesm2022/ng-doc-app-components-image-viewer.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-kind-icon.mjs +42 -25
- package/fesm2022/ng-doc-app-components-kind-icon.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-link.mjs +12 -9
- package/fesm2022/ng-doc-app-components-link.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-members.mjs +30 -0
- package/fesm2022/ng-doc-app-components-members.mjs.map +1 -0
- package/fesm2022/ng-doc-app-components-mermaid-viewer.mjs +61 -20
- package/fesm2022/ng-doc-app-components-mermaid-viewer.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-navbar.mjs +39 -49
- package/fesm2022/ng-doc-app-components-navbar.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page-header.mjs +73 -10
- package/fesm2022/ng-doc-app-components-page-header.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page-link.mjs +123 -31
- package/fesm2022/ng-doc-app-components-page-link.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page-navigation.mjs +17 -19
- package/fesm2022/ng-doc-app-components-page-navigation.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page-wrapper.mjs +56 -35
- package/fesm2022/ng-doc-app-components-page-wrapper.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page.mjs +168 -24
- package/fesm2022/ng-doc-app-components-page.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-playground.mjs +477 -286
- package/fesm2022/ng-doc-app-components-playground.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-root.mjs +48 -31
- package/fesm2022/ng-doc-app-components-root.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-search-result.mjs +22 -21
- package/fesm2022/ng-doc-app-components-search-result.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-search.mjs +64 -33
- package/fesm2022/ng-doc-app-components-search.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-sidebar.mjs +86 -61
- package/fesm2022/ng-doc-app-components-sidebar.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-tabs.mjs +35 -11
- package/fesm2022/ng-doc-app-components-tabs.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-theme-toggle.mjs +33 -21
- package/fesm2022/ng-doc-app-components-theme-toggle.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-toc.mjs +267 -90
- package/fesm2022/ng-doc-app-components-toc.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components.mjs +4 -1
- package/fesm2022/ng-doc-app-components.mjs.map +1 -1
- package/fesm2022/ng-doc-app-defaults-default-processors.mjs +2 -1
- package/fesm2022/ng-doc-app-defaults-default-processors.mjs.map +1 -1
- package/fesm2022/ng-doc-app-demo-app.mjs +176 -0
- package/fesm2022/ng-doc-app-demo-app.mjs.map +1 -0
- package/fesm2022/ng-doc-app-directives-code-highlighter.mjs +11 -5
- package/fesm2022/ng-doc-app-directives-code-highlighter.mjs.map +1 -1
- package/fesm2022/ng-doc-app-directives-members-filter.mjs +276 -0
- package/fesm2022/ng-doc-app-directives-members-filter.mjs.map +1 -0
- package/fesm2022/ng-doc-app-directives-route-active.mjs +34 -30
- package/fesm2022/ng-doc-app-directives-route-active.mjs.map +1 -1
- package/fesm2022/ng-doc-app-directives.mjs +1 -0
- package/fesm2022/ng-doc-app-directives.mjs.map +1 -1
- package/fesm2022/ng-doc-app-helpers.mjs +307 -2
- package/fesm2022/ng-doc-app-helpers.mjs.map +1 -1
- package/fesm2022/ng-doc-app-pipes-decode-uri-component.mjs +6 -4
- package/fesm2022/ng-doc-app-pipes-decode-uri-component.mjs.map +1 -1
- package/fesm2022/ng-doc-app-pipes-extract-value.mjs +6 -4
- package/fesm2022/ng-doc-app-pipes-extract-value.mjs.map +1 -1
- package/fesm2022/ng-doc-app-pipes-filter-by-text.mjs +7 -4
- package/fesm2022/ng-doc-app-pipes-filter-by-text.mjs.map +1 -1
- package/fesm2022/ng-doc-app-pipes-sanitize-html.mjs +8 -5
- package/fesm2022/ng-doc-app-pipes-sanitize-html.mjs.map +1 -1
- package/fesm2022/ng-doc-app-processors-page-processor.mjs +86 -23
- package/fesm2022/ng-doc-app-processors-page-processor.mjs.map +1 -1
- package/fesm2022/ng-doc-app-processors-processors-tooltip.mjs +26 -25
- package/fesm2022/ng-doc-app-processors-processors-tooltip.mjs.map +1 -1
- package/fesm2022/ng-doc-app-processors-processors.mjs +42 -10
- package/fesm2022/ng-doc-app-processors-processors.mjs.map +1 -1
- package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs +31 -2
- package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs.map +1 -1
- package/fesm2022/ng-doc-app-providers-title.mjs +129 -0
- package/fesm2022/ng-doc-app-providers-title.mjs.map +1 -0
- package/fesm2022/ng-doc-app-providers.mjs +1 -0
- package/fesm2022/ng-doc-app-providers.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs +122 -0
- package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs.map +1 -0
- package/fesm2022/ng-doc-app-services-content-state.mjs +57 -0
- package/fesm2022/ng-doc-app-services-content-state.mjs.map +1 -0
- package/fesm2022/ng-doc-app-services-fullscreen-route.mjs +74 -0
- package/fesm2022/ng-doc-app-services-fullscreen-route.mjs.map +1 -0
- package/fesm2022/ng-doc-app-services-highlighter.mjs +90 -17
- package/fesm2022/ng-doc-app-services-highlighter.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services-route-preloader.mjs +384 -0
- package/fesm2022/ng-doc-app-services-route-preloader.mjs.map +1 -0
- package/fesm2022/ng-doc-app-services-shortcuts.mjs +214 -0
- package/fesm2022/ng-doc-app-services-shortcuts.mjs.map +1 -0
- package/fesm2022/ng-doc-app-services-sidebar.mjs +59 -25
- package/fesm2022/ng-doc-app-services-sidebar.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services-store.mjs +8 -8
- package/fesm2022/ng-doc-app-services-store.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services-theme.mjs +32 -12
- package/fesm2022/ng-doc-app-services-theme.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services.mjs +143 -0
- package/fesm2022/ng-doc-app-services.mjs.map +1 -1
- package/fesm2022/ng-doc-app-tokens.mjs +7 -28
- package/fesm2022/ng-doc-app-tokens.mjs.map +1 -1
- package/fesm2022/ng-doc-app-type-controls-boolean-control.mjs +18 -9
- package/fesm2022/ng-doc-app-type-controls-boolean-control.mjs.map +1 -1
- package/fesm2022/ng-doc-app-type-controls-number-control.mjs +12 -9
- package/fesm2022/ng-doc-app-type-controls-number-control.mjs.map +1 -1
- package/fesm2022/ng-doc-app-type-controls-string-control.mjs +12 -9
- package/fesm2022/ng-doc-app-type-controls-string-control.mjs.map +1 -1
- package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs +59 -18
- package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs.map +1 -1
- package/package.json +82 -28
- package/styles/global.css +1128 -201
- package/styles/main.css +1128 -201
- package/styles/themes/dark.css +88 -56
- package/types/ng-doc-app-classes-content-anchor-controller.d.ts +54 -0
- package/types/ng-doc-app-classes-content-controller.d.ts +94 -0
- package/types/ng-doc-app-classes-default-search-engine.d.ts +9 -1
- package/types/ng-doc-app-classes-root-page.d.ts +14 -1
- package/types/ng-doc-app-classes-search-engine.d.ts +5 -0
- package/types/ng-doc-app-classes.d.ts +2 -0
- package/types/ng-doc-app-components-api-list.d.ts +69 -20
- package/types/ng-doc-app-components-breadcrumb.d.ts +8 -3
- package/types/ng-doc-app-components-code.d.ts +46 -12
- package/types/ng-doc-app-components-command-palette.d.ts +147 -0
- package/types/ng-doc-app-components-copy-button.d.ts +18 -6
- package/types/ng-doc-app-components-demo-displayer.d.ts +23 -10
- package/types/ng-doc-app-components-demo-frame.d.ts +48 -0
- package/types/ng-doc-app-components-demo-pane.d.ts +19 -15
- package/types/ng-doc-app-components-demo.d.ts +80 -15
- package/types/ng-doc-app-components-fullscreen-button.d.ts +4 -2
- package/types/ng-doc-app-components-fullscreen-toggle.d.ts +24 -0
- package/types/ng-doc-app-components-heading-anchor.d.ts +26 -7
- package/types/ng-doc-app-components-image-viewer.d.ts +71 -7
- package/types/ng-doc-app-components-kind-icon.d.ts +28 -7
- package/types/ng-doc-app-components-link.d.ts +6 -2
- package/types/ng-doc-app-components-members.d.ts +13 -0
- package/types/ng-doc-app-components-mermaid-viewer.d.ts +21 -6
- package/types/ng-doc-app-components-navbar.d.ts +25 -15
- package/types/ng-doc-app-components-page-header.d.ts +28 -4
- package/types/ng-doc-app-components-page-link.d.ts +34 -15
- package/types/ng-doc-app-components-page-navigation.d.ts +10 -5
- package/types/ng-doc-app-components-page-wrapper.d.ts +35 -11
- package/types/ng-doc-app-components-page.d.ts +57 -6
- package/types/ng-doc-app-components-playground.d.ts +167 -78
- package/types/ng-doc-app-components-root.d.ts +28 -7
- package/types/ng-doc-app-components-search-result.d.ts +14 -6
- package/types/ng-doc-app-components-search.d.ts +19 -7
- package/types/ng-doc-app-components-tabs.d.ts +24 -5
- package/types/ng-doc-app-components-theme-toggle.d.ts +18 -5
- package/types/ng-doc-app-components-toc.d.ts +76 -29
- package/types/ng-doc-app-components.d.ts +4 -1
- package/types/ng-doc-app-demo-app.d.ts +89 -0
- package/types/ng-doc-app-directives-code-highlighter.d.ts +8 -4
- package/types/ng-doc-app-directives-members-filter.d.ts +61 -0
- package/types/ng-doc-app-directives-route-active.d.ts +21 -7
- package/types/ng-doc-app-directives.d.ts +1 -0
- package/types/ng-doc-app-helpers.d.ts +142 -3
- package/types/ng-doc-app-interfaces.d.ts +62 -31
- package/types/ng-doc-app-pipes-decode-uri-component.d.ts +3 -0
- package/types/ng-doc-app-pipes-extract-value.d.ts +3 -0
- package/types/ng-doc-app-pipes-filter-by-text.d.ts +4 -0
- package/types/ng-doc-app-pipes-sanitize-html.d.ts +4 -2
- package/types/ng-doc-app-processors-page-processor.d.ts +39 -10
- package/types/ng-doc-app-processors-processors-tooltip.d.ts +11 -10
- package/types/ng-doc-app-processors-processors.d.ts +10 -1
- package/types/ng-doc-app-providers-ng-doc-app.d.ts +18 -1
- package/types/ng-doc-app-providers-title.d.ts +59 -0
- package/types/ng-doc-app-providers.d.ts +1 -0
- package/types/ng-doc-app-services-content-scroll-intent.d.ts +52 -0
- package/types/ng-doc-app-services-content-state.d.ts +40 -0
- package/types/ng-doc-app-services-fullscreen-route.d.ts +35 -0
- package/types/ng-doc-app-services-highlighter.d.ts +47 -5
- package/types/ng-doc-app-services-route-preloader.d.ts +121 -0
- package/types/ng-doc-app-services-shortcuts.d.ts +96 -0
- package/types/ng-doc-app-services-sidebar.d.ts +24 -4
- package/types/ng-doc-app-services-store.d.ts +24 -1
- package/types/ng-doc-app-services-theme.d.ts +19 -3
- package/types/ng-doc-app-services.d.ts +77 -0
- package/types/ng-doc-app-tokens.d.ts +10 -30
- package/types/ng-doc-app-type-controls-boolean-control.d.ts +11 -6
- package/types/ng-doc-app-type-controls-number-control.d.ts +5 -3
- package/types/ng-doc-app-type-controls-string-control.d.ts +5 -3
- package/types/ng-doc-app-type-controls-type-alias-control.d.ts +35 -6
- package/fesm2022/ng-doc-app-components-search-dialog.mjs +0 -78
- package/fesm2022/ng-doc-app-components-search-dialog.mjs.map +0 -1
- 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,
|
|
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
|
|
9
|
-
import {
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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
|
-
|
|
69
|
-
|
|
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.
|
|
72
|
-
type:
|
|
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,
|
|
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,
|
|
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.
|
|
19
|
-
static { this.ɵprov = i0.ɵɵ
|
|
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.
|
|
22
|
-
type:
|
|
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,
|
|
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,
|
|
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
|
|
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
|
-
|
|
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,
|
|
35
|
-
this.
|
|
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.
|
|
38
|
-
static { this.ɵprov = i0.ɵɵ
|
|
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.
|
|
41
|
-
type:
|
|
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,
|
|
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;;;;"}
|