@ng-doc/app 22.1.0 → 22.2.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/LICENSE +21 -0
- package/fesm2022/ng-doc-app-classes-content-controller.mjs +22 -4
- package/fesm2022/ng-doc-app-classes-content-controller.mjs.map +1 -1
- package/fesm2022/ng-doc-app-classes-default-search-engine.mjs +36 -12
- 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-components-api-list.mjs +7 -5
- package/fesm2022/ng-doc-app-components-api-list.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-breadcrumb.mjs +5 -3
- package/fesm2022/ng-doc-app-components-breadcrumb.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-code.mjs +6 -4
- package/fesm2022/ng-doc-app-components-code.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-command-palette.mjs +35 -27
- package/fesm2022/ng-doc-app-components-command-palette.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-copy-button.mjs +6 -4
- package/fesm2022/ng-doc-app-components-copy-button.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-demo-displayer.mjs +19 -4
- package/fesm2022/ng-doc-app-components-demo-displayer.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-demo-frame.mjs +195 -12
- package/fesm2022/ng-doc-app-components-demo-frame.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-demo-pane.mjs +2 -2
- package/fesm2022/ng-doc-app-components-demo-pane.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-demo.mjs +49 -12
- package/fesm2022/ng-doc-app-components-demo.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-fullscreen-button.mjs +5 -3
- package/fesm2022/ng-doc-app-components-fullscreen-button.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs +4 -2
- package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-heading-anchor.mjs +5 -3
- package/fesm2022/ng-doc-app-components-heading-anchor.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-image-viewer.mjs +5 -3
- package/fesm2022/ng-doc-app-components-image-viewer.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-navbar.mjs +4 -2
- package/fesm2022/ng-doc-app-components-navbar.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page-navigation.mjs +5 -3
- package/fesm2022/ng-doc-app-components-page-navigation.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page-wrapper.mjs +2 -2
- package/fesm2022/ng-doc-app-components-page-wrapper.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-page.mjs +83 -16
- package/fesm2022/ng-doc-app-components-page.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-playground.mjs +743 -108
- package/fesm2022/ng-doc-app-components-playground.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-root.mjs +51 -4
- package/fesm2022/ng-doc-app-components-root.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-search-result.mjs +8 -6
- package/fesm2022/ng-doc-app-components-search-result.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-search.mjs +7 -5
- package/fesm2022/ng-doc-app-components-search.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-sidebar.mjs +4 -3
- package/fesm2022/ng-doc-app-components-sidebar.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-theme-toggle.mjs +7 -5
- package/fesm2022/ng-doc-app-components-theme-toggle.mjs.map +1 -1
- package/fesm2022/ng-doc-app-components-toc.mjs +4 -2
- package/fesm2022/ng-doc-app-components-toc.mjs.map +1 -1
- package/fesm2022/ng-doc-app-constants.mjs +11 -1
- package/fesm2022/ng-doc-app-constants.mjs.map +1 -1
- package/fesm2022/ng-doc-app-demo-app.mjs +618 -15
- package/fesm2022/ng-doc-app-demo-app.mjs.map +1 -1
- package/fesm2022/ng-doc-app-helpers.mjs +125 -6
- package/fesm2022/ng-doc-app-helpers.mjs.map +1 -1
- package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs +29 -18
- package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs.map +1 -1
- package/fesm2022/ng-doc-app-providers-texts.mjs +53 -0
- package/fesm2022/ng-doc-app-providers-texts.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 +27 -2
- package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services-highlighter.mjs +8 -5
- package/fesm2022/ng-doc-app-services-highlighter.mjs.map +1 -1
- package/fesm2022/ng-doc-app-services-shortcuts.mjs +38 -4
- package/fesm2022/ng-doc-app-services-shortcuts.mjs.map +1 -1
- package/fesm2022/ng-doc-app-texts-ar.mjs +238 -0
- package/fesm2022/ng-doc-app-texts-ar.mjs.map +1 -0
- package/fesm2022/ng-doc-app-texts-de.mjs +205 -0
- package/fesm2022/ng-doc-app-texts-de.mjs.map +1 -0
- package/fesm2022/ng-doc-app-texts-es.mjs +205 -0
- package/fesm2022/ng-doc-app-texts-es.mjs.map +1 -0
- package/fesm2022/ng-doc-app-texts-fr.mjs +205 -0
- package/fesm2022/ng-doc-app-texts-fr.mjs.map +1 -0
- package/fesm2022/ng-doc-app-texts-hi.mjs +219 -0
- package/fesm2022/ng-doc-app-texts-hi.mjs.map +1 -0
- package/fesm2022/ng-doc-app-texts-ja.mjs +205 -0
- package/fesm2022/ng-doc-app-texts-ja.mjs.map +1 -0
- package/fesm2022/ng-doc-app-texts-ko.mjs +205 -0
- package/fesm2022/ng-doc-app-texts-ko.mjs.map +1 -0
- package/fesm2022/ng-doc-app-texts-pt-br.mjs +205 -0
- package/fesm2022/ng-doc-app-texts-pt-br.mjs.map +1 -0
- package/fesm2022/ng-doc-app-texts-ru.mjs +231 -0
- package/fesm2022/ng-doc-app-texts-ru.mjs.map +1 -0
- package/fesm2022/ng-doc-app-texts-uk.mjs +231 -0
- package/fesm2022/ng-doc-app-texts-uk.mjs.map +1 -0
- package/fesm2022/ng-doc-app-texts-zh.mjs +205 -0
- package/fesm2022/ng-doc-app-texts-zh.mjs.map +1 -0
- package/fesm2022/ng-doc-app-tokens.mjs +214 -1
- package/fesm2022/ng-doc-app-tokens.mjs.map +1 -1
- package/fesm2022/ng-doc-app-type-controls-json-control.mjs +90 -0
- package/fesm2022/ng-doc-app-type-controls-json-control.mjs.map +1 -0
- package/fesm2022/ng-doc-app-type-controls-list-control.mjs +792 -0
- package/fesm2022/ng-doc-app-type-controls-list-control.mjs.map +1 -0
- package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs +5 -3
- package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs.map +1 -1
- package/fesm2022/ng-doc-app-type-controls.mjs +2 -0
- package/fesm2022/ng-doc-app-type-controls.mjs.map +1 -1
- package/package.json +59 -3
- package/styles/global.css +1 -0
- package/styles/main.css +1 -0
- package/types/ng-doc-app-classes-default-search-engine.d.ts +11 -0
- package/types/ng-doc-app-classes-root-page.d.ts +5 -0
- package/types/ng-doc-app-components-api-list.d.ts +3 -1
- package/types/ng-doc-app-components-breadcrumb.d.ts +2 -1
- package/types/ng-doc-app-components-code.d.ts +2 -0
- package/types/ng-doc-app-components-command-palette.d.ts +3 -1
- package/types/ng-doc-app-components-copy-button.d.ts +3 -1
- package/types/ng-doc-app-components-demo-displayer.d.ts +13 -1
- package/types/ng-doc-app-components-demo-frame.d.ts +62 -3
- package/types/ng-doc-app-components-demo.d.ts +27 -8
- package/types/ng-doc-app-components-fullscreen-button.d.ts +2 -0
- package/types/ng-doc-app-components-fullscreen-toggle.d.ts +2 -0
- package/types/ng-doc-app-components-heading-anchor.d.ts +2 -0
- package/types/ng-doc-app-components-image-viewer.d.ts +2 -0
- package/types/ng-doc-app-components-navbar.d.ts +2 -0
- package/types/ng-doc-app-components-page-navigation.d.ts +2 -1
- package/types/ng-doc-app-components-page.d.ts +10 -1
- package/types/ng-doc-app-components-playground.d.ts +241 -44
- package/types/ng-doc-app-components-root.d.ts +18 -0
- package/types/ng-doc-app-components-search-result.d.ts +2 -0
- package/types/ng-doc-app-components-search.d.ts +2 -0
- package/types/ng-doc-app-components-sidebar.d.ts +2 -1
- package/types/ng-doc-app-components-theme-toggle.d.ts +4 -1
- package/types/ng-doc-app-components-toc.d.ts +2 -1
- package/types/ng-doc-app-constants.d.ts +11 -1
- package/types/ng-doc-app-demo-app.d.ts +211 -3
- package/types/ng-doc-app-helpers.d.ts +56 -3
- package/types/ng-doc-app-interfaces.d.ts +621 -3
- package/types/ng-doc-app-providers-ng-doc-app.d.ts +6 -0
- package/types/ng-doc-app-providers-texts.d.ts +28 -0
- package/types/ng-doc-app-providers.d.ts +1 -0
- package/types/ng-doc-app-services-content-scroll-intent.d.ts +11 -1
- package/types/ng-doc-app-services-highlighter.d.ts +3 -1
- package/types/ng-doc-app-services-shortcuts.d.ts +16 -0
- package/types/ng-doc-app-texts-ar.d.ts +13 -0
- package/types/ng-doc-app-texts-de.d.ts +12 -0
- package/types/ng-doc-app-texts-es.d.ts +12 -0
- package/types/ng-doc-app-texts-fr.d.ts +12 -0
- package/types/ng-doc-app-texts-hi.d.ts +12 -0
- package/types/ng-doc-app-texts-ja.d.ts +12 -0
- package/types/ng-doc-app-texts-ko.d.ts +12 -0
- package/types/ng-doc-app-texts-pt-br.d.ts +12 -0
- package/types/ng-doc-app-texts-ru.d.ts +12 -0
- package/types/ng-doc-app-texts-uk.d.ts +12 -0
- package/types/ng-doc-app-texts-zh.d.ts +12 -0
- package/types/ng-doc-app-tokens.d.ts +21 -2
- package/types/ng-doc-app-type-controls-json-control.d.ts +38 -0
- package/types/ng-doc-app-type-controls-list-control.d.ts +313 -0
- package/types/ng-doc-app-type-controls-type-alias-control.d.ts +2 -1
- package/types/ng-doc-app-type-controls.d.ts +2 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-doc-app-providers-ng-doc-app.mjs","sources":["../../../../libs/app/providers/ng-doc-app/provide-ng-doc-app.ts","../../../../libs/app/providers/ng-doc-app/ng-doc-app-providers-ng-doc-app.ts"],"sourcesContent":["import { isPlatformBrowser
|
|
1
|
+
{"version":3,"file":"ng-doc-app-providers-ng-doc-app.mjs","sources":["../../../../libs/app/providers/ng-doc-app/provide-ng-doc-app.ts","../../../../libs/app/providers/ng-doc-app/ng-doc-app-providers-ng-doc-app.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n DOCUMENT,\n EnvironmentProviders,\n inject,\n PendingTasks,\n PLATFORM_ID,\n provideEnvironmentInitializer,\n Provider,\n} from '@angular/core';\nimport { ɵcaptureNgDocHydrationSnapshots } from '@ng-doc/app/helpers';\nimport { NgDocApiListIndexService } from '@ng-doc/app/services';\nimport { NgDocContentScrollIntent } from '@ng-doc/app/services/content-scroll-intent';\nimport type { NgDocHighlighterConfig } from '@ng-doc/app/services/highlighter';\nimport { NgDocHighlighterService } from '@ng-doc/app/services/highlighter';\nimport { NG_DOC_SHORTCUTS, NgDocShortcutsService } from '@ng-doc/app/services/shortcuts';\nimport type { NgDocContentScrollPositionRestoration } from '@ng-doc/app/tokens';\nimport {\n NG_DOC_CONTENT_ANCHOR_SCROLLING,\n NG_DOC_CONTENT_SCROLL_POSITION_RESTORATION,\n} from '@ng-doc/app/tokens';\nimport type { NgDocUiConfig } from '@ng-doc/ui-kit';\nimport { provideNgDocUiKitConfig } from '@ng-doc/ui-kit';\n\n/**\n * NgDoc application config.\n */\nexport interface NgDocApplicationConfig {\n /**\n * Replays a router anchor scroll once asynchronous NgDoc content has been processed.\n * Enable this beside Angular router anchor scrolling.\n */\n contentAnchorScrolling?: boolean;\n /**\n * Replays a saved router position after asynchronous NgDoc content is processed.\n * Select the same intent as Angular router scroll position restoration.\n */\n contentScrollPositionRestoration?: NgDocContentScrollPositionRestoration;\n /**\n * UI Kit configuration.\n */\n uiKit?: NgDocUiConfig;\n /**\n * Extra Shiki themes and languages for the code that the browser highlights.\n */\n shiki?: NgDocHighlighterConfig;\n /**\n * Whether single-key shortcuts (slash, the square brackets, F, L and T) are on for readers who\n * have not turned them on or off themselves. Readers can always change it with the switch in\n * the search palette. Command+K (Control+K) opens the search either way. Defaults to true.\n */\n shortcuts?: boolean;\n}\n\n/**\n * Provides the NgDoc application configuration.\n *\n * Add it to the application's providers, or to the `providers` of the route that holds the\n * documentation, together with the other NgDoc providers: then NgDoc starts only when the reader\n * opens the documentation, and its code stays out of the application's initial bundle. It starts\n * with environment initializers, which run where it is provided. The server render waits until\n * the code highlighter of playgrounds has loaded.\n * @param config - The optional application configuration.\n */\nexport function provideNgDocApp(\n config?: NgDocApplicationConfig,\n): Array<EnvironmentProviders | Provider> {\n return [\n { provide: NG_DOC_CONTENT_ANCHOR_SCROLLING, useValue: config?.contentAnchorScrolling ?? false },\n {\n provide: NG_DOC_CONTENT_SCROLL_POSITION_RESTORATION,\n useValue: config?.contentScrollPositionRestoration ?? 'disabled',\n },\n { provide: NG_DOC_SHORTCUTS, useValue: config?.shortcuts ?? true },\n\n /* --- Services --- */\n // Provided where the configuration is, so that they read the NgDoc providers of a lazy route\n // (the generated context, the shortcuts option): an instance in the root injector could not\n // see them. In the root injector this is the instance `providedIn: 'root'` gives.\n NgDocHighlighterService,\n NgDocShortcutsService,\n NgDocApiListIndexService,\n\n ...(config?.contentAnchorScrolling || config?.contentScrollPositionRestoration === 'enabled'\n ? [\n provideEnvironmentInitializer(() => {\n inject(NgDocContentScrollIntent).start();\n }),\n ]\n : []),\n\n /* --- Hydration --- */\n // Before the documentation renders: the pages and headers that skip hydration show their\n // server-rendered content until their own is rendered, instead of a blank page. A lazy route\n // creates its injector before it creates its components, so this also runs in time when the\n // documentation is the first route the browser hydrates.\n provideEnvironmentInitializer(() => {\n if (isPlatformBrowser(inject(PLATFORM_ID))) ɵcaptureNgDocHydrationSnapshots(inject(DOCUMENT));\n }),\n\n /* --- Shiki --- */\n // Started with the documentation instead of blocking the application's start: the playgrounds\n // highlight their code once it is ready, and the application stays unstable until then, so the\n // server renders the highlighted code.\n provideEnvironmentInitializer(() => {\n const highlighter = inject(NgDocHighlighterService);\n\n inject(PendingTasks).run(() => highlighter.initialize(config?.shiki));\n }),\n\n /* --- UiKit --- */\n ...provideNgDocUiKitConfig(config?.uiKit),\n ];\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["ɵcaptureNgDocHydrationSnapshots"],"mappings":";;;;;;;;;;AAsDA;;;;;;;;;AASG;AACG,SAAU,eAAe,CAC7B,MAA+B,EAAA;IAE/B,OAAO;QACL,EAAE,OAAO,EAAE,+BAA+B,EAAE,QAAQ,EAAE,MAAM,EAAE,sBAAsB,IAAI,KAAK,EAAE;AAC/F,QAAA;AACE,YAAA,OAAO,EAAE,0CAA0C;AACnD,YAAA,QAAQ,EAAE,MAAM,EAAE,gCAAgC,IAAI,UAAU;AACjE,SAAA;QACD,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,IAAI,IAAI,EAAE;;;;;QAMlE,uBAAuB;QACvB,qBAAqB;QACrB,wBAAwB;QAExB,IAAI,MAAM,EAAE,sBAAsB,IAAI,MAAM,EAAE,gCAAgC,KAAK;AACjF,cAAE;gBACE,6BAA6B,CAAC,MAAK;AACjC,oBAAA,MAAM,CAAC,wBAAwB,CAAC,CAAC,KAAK,EAAE;AAC1C,gBAAA,CAAC,CAAC;AACH;cACD,EAAE,CAAC;;;;;;QAOP,6BAA6B,CAAC,MAAK;AACjC,YAAA,IAAI,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAAE,gBAAAA,+BAA+B,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC/F,QAAA,CAAC,CAAC;;;;;QAMF,6BAA6B,CAAC,MAAK;AACjC,YAAA,MAAM,WAAW,GAAG,MAAM,CAAC,uBAAuB,CAAC;AAEnD,YAAA,MAAM,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,MAAM,WAAW,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AACvE,QAAA,CAAC,CAAC;;AAGF,QAAA,GAAG,uBAAuB,CAAC,MAAM,EAAE,KAAK,CAAC;KAC1C;AACH;;ACjHA;;AAEG;;;;"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { InjectionToken, inject } from '@angular/core';
|
|
2
|
+
import { NG_DOC_TEXTS, mergeNgDocTexts, NG_DOC_DEFAULT_TEXTS } from '@ng-doc/app/tokens';
|
|
3
|
+
import { NG_DOC_UI_KIT_TEXTS } from '@ng-doc/ui-kit/texts';
|
|
4
|
+
|
|
5
|
+
/** The changes of every `provideNgDocTexts` call of one injector, in the order of the calls. */
|
|
6
|
+
const NG_DOC_TEXTS_OVERRIDES = new InjectionToken('NG_DOC_TEXTS_OVERRIDES');
|
|
7
|
+
/**
|
|
8
|
+
* Changes the texts NgDoc shows to readers, for example to translate them. Pass the groups and
|
|
9
|
+
* texts to change; every other text keeps its English default (`NG_DOC_DEFAULT_TEXTS`). NgDoc
|
|
10
|
+
* ships complete translations in entry points of their own, such as `NG_DOC_TEXTS_DE` from
|
|
11
|
+
* `@ng-doc/app/texts/de`; pass one first and your changes after it.
|
|
12
|
+
*
|
|
13
|
+
* Calls and arguments add up: in one injector they apply in order, so a later one wins for a
|
|
14
|
+
* text both set, and the providers of a child injector (a lazy route, for example) apply on top of the
|
|
15
|
+
* texts of its parent. The texts are read when a component is created, so they are fixed per
|
|
16
|
+
* injector: switch the language by reloading the application, not at run time.
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* provideNgDocTexts(NG_DOC_TEXTS_DE, { search: { placeholder: 'Handbuch durchsuchen…' } });
|
|
20
|
+
*
|
|
21
|
+
* provideNgDocTexts({
|
|
22
|
+
* search: { placeholder: 'Dokumentation durchsuchen…' },
|
|
23
|
+
* searchPalette: { noResults: ({ query }) => `Keine Ergebnisse für „${query}“` },
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
* @param texts - The texts to change, by group, in the order they apply.
|
|
27
|
+
* @returns The providers to add to the application, a route or a page.
|
|
28
|
+
*/
|
|
29
|
+
function provideNgDocTexts(...texts) {
|
|
30
|
+
return [
|
|
31
|
+
...texts.map((overrides) => ({
|
|
32
|
+
provide: NG_DOC_TEXTS_OVERRIDES,
|
|
33
|
+
useValue: overrides,
|
|
34
|
+
multi: true,
|
|
35
|
+
})),
|
|
36
|
+
{ provide: NG_DOC_TEXTS, useFactory: resolveTexts },
|
|
37
|
+
// The ui-kit components read their own token: NgDocTexts extends NgDocUiKitTexts, so the
|
|
38
|
+
// same object serves both and the two libraries always show the same texts.
|
|
39
|
+
{ provide: NG_DOC_UI_KIT_TEXTS, useFactory: () => inject(NG_DOC_TEXTS) },
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
/** The texts of the parent injector, or the defaults, with this injector's changes on top. */
|
|
43
|
+
function resolveTexts() {
|
|
44
|
+
const parent = inject(NG_DOC_TEXTS, { skipSelf: true, optional: true });
|
|
45
|
+
return mergeNgDocTexts(parent ?? NG_DOC_DEFAULT_TEXTS, ...inject(NG_DOC_TEXTS_OVERRIDES, { self: true }));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generated bundle index. Do not edit.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
export { provideNgDocTexts };
|
|
53
|
+
//# sourceMappingURL=ng-doc-app-providers-texts.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-doc-app-providers-texts.mjs","sources":["../../../../libs/app/providers/texts/provide-ng-doc-texts.ts","../../../../libs/app/providers/texts/ng-doc-app-providers-texts.ts"],"sourcesContent":["import { inject, InjectionToken, Provider } from '@angular/core';\nimport type { NgDocTexts, NgDocTextsOverrides } from '@ng-doc/app/interfaces';\nimport { mergeNgDocTexts, NG_DOC_DEFAULT_TEXTS, NG_DOC_TEXTS } from '@ng-doc/app/tokens';\nimport { NG_DOC_UI_KIT_TEXTS } from '@ng-doc/ui-kit/texts';\n\n/** The changes of every `provideNgDocTexts` call of one injector, in the order of the calls. */\nconst NG_DOC_TEXTS_OVERRIDES = new InjectionToken<NgDocTextsOverrides[]>('NG_DOC_TEXTS_OVERRIDES');\n\n/**\n * Changes the texts NgDoc shows to readers, for example to translate them. Pass the groups and\n * texts to change; every other text keeps its English default (`NG_DOC_DEFAULT_TEXTS`). NgDoc\n * ships complete translations in entry points of their own, such as `NG_DOC_TEXTS_DE` from\n * `@ng-doc/app/texts/de`; pass one first and your changes after it.\n *\n * Calls and arguments add up: in one injector they apply in order, so a later one wins for a\n * text both set, and the providers of a child injector (a lazy route, for example) apply on top of the\n * texts of its parent. The texts are read when a component is created, so they are fixed per\n * injector: switch the language by reloading the application, not at run time.\n * @example\n * ```typescript\n * provideNgDocTexts(NG_DOC_TEXTS_DE, { search: { placeholder: 'Handbuch durchsuchen…' } });\n *\n * provideNgDocTexts({\n * search: { placeholder: 'Dokumentation durchsuchen…' },\n * searchPalette: { noResults: ({ query }) => `Keine Ergebnisse für „${query}“` },\n * });\n * ```\n * @param texts - The texts to change, by group, in the order they apply.\n * @returns The providers to add to the application, a route or a page.\n */\nexport function provideNgDocTexts(...texts: NgDocTextsOverrides[]): Provider[] {\n return [\n ...texts.map((overrides: NgDocTextsOverrides) => ({\n provide: NG_DOC_TEXTS_OVERRIDES,\n useValue: overrides,\n multi: true,\n })),\n { provide: NG_DOC_TEXTS, useFactory: resolveTexts },\n // The ui-kit components read their own token: NgDocTexts extends NgDocUiKitTexts, so the\n // same object serves both and the two libraries always show the same texts.\n { provide: NG_DOC_UI_KIT_TEXTS, useFactory: () => inject(NG_DOC_TEXTS) },\n ];\n}\n\n/** The texts of the parent injector, or the defaults, with this injector's changes on top. */\nfunction resolveTexts(): NgDocTexts {\n const parent: NgDocTexts | null = inject(NG_DOC_TEXTS, { skipSelf: true, optional: true });\n\n return mergeNgDocTexts(\n parent ?? NG_DOC_DEFAULT_TEXTS,\n ...inject(NG_DOC_TEXTS_OVERRIDES, { self: true }),\n );\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;AAKA;AACA,MAAM,sBAAsB,GAAG,IAAI,cAAc,CAAwB,wBAAwB,CAAC;AAElG;;;;;;;;;;;;;;;;;;;;;AAqBG;AACG,SAAU,iBAAiB,CAAC,GAAG,KAA4B,EAAA;IAC/D,OAAO;QACL,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,SAA8B,MAAM;AAChD,YAAA,OAAO,EAAE,sBAAsB;AAC/B,YAAA,QAAQ,EAAE,SAAS;AACnB,YAAA,KAAK,EAAE,IAAI;AACZ,SAAA,CAAC,CAAC;AACH,QAAA,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE;;;AAGnD,QAAA,EAAE,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC,YAAY,CAAC,EAAE;KACzE;AACH;AAEA;AACA,SAAS,YAAY,GAAA;AACnB,IAAA,MAAM,MAAM,GAAsB,MAAM,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAE1F,IAAA,OAAO,eAAe,CACpB,MAAM,IAAI,oBAAoB,EAC9B,GAAG,MAAM,CAAC,sBAAsB,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAClD;AACH;;ACpDA;;AAEG;;;;"}
|
|
@@ -3,6 +3,7 @@ export * from '@ng-doc/app/providers/ng-doc-app';
|
|
|
3
3
|
export * from '@ng-doc/app/providers/page-skeleton';
|
|
4
4
|
export * from '@ng-doc/app/providers/playground-demo';
|
|
5
5
|
export * from '@ng-doc/app/providers/search-engine';
|
|
6
|
+
export * from '@ng-doc/app/providers/texts';
|
|
6
7
|
export * from '@ng-doc/app/providers/title';
|
|
7
8
|
export * from '@ng-doc/app/providers/type-control';
|
|
8
9
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-doc-app-providers.mjs","sources":["../../../../libs/app/providers/ng-doc-app-providers.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ng-doc-app-providers.mjs","sources":["../../../../libs/app/providers/ng-doc-app-providers.ts"],"sourcesContent":["/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;;;;;AAAA;;AAEG"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isPlatformBrowser } from '@angular/common';
|
|
1
|
+
import { isPlatformBrowser, Location } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
3
|
import { inject, PLATFORM_ID, signal, DestroyRef, Service } from '@angular/core';
|
|
4
4
|
import { Router, NavigationStart, NavigationEnd, Scroll, NavigationCancel, NavigationError, NavigationSkipped } from '@angular/router';
|
|
@@ -8,6 +8,7 @@ class NgDocContentScrollIntent {
|
|
|
8
8
|
constructor() {
|
|
9
9
|
this.browser = isPlatformBrowser(inject(PLATFORM_ID));
|
|
10
10
|
this.router = inject(Router);
|
|
11
|
+
this.location = inject(Location);
|
|
11
12
|
this.listeners = new Set();
|
|
12
13
|
this.started = false;
|
|
13
14
|
this.successful = false;
|
|
@@ -19,11 +20,15 @@ class NgDocContentScrollIntent {
|
|
|
19
20
|
this.currentIntent = this.intentState.asReadonly();
|
|
20
21
|
inject(DestroyRef).onDestroy(() => this.destroy());
|
|
21
22
|
}
|
|
22
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Starts following router events. Does nothing on the server or when already started. Started
|
|
25
|
+
* during a navigation, it follows that navigation too.
|
|
26
|
+
*/
|
|
23
27
|
start() {
|
|
24
28
|
if (this.started || !this.browser)
|
|
25
29
|
return;
|
|
26
30
|
this.started = true;
|
|
31
|
+
this.followCurrentNavigation();
|
|
27
32
|
this.subscription = this.router.events.subscribe((event) => {
|
|
28
33
|
if (event instanceof NavigationStart)
|
|
29
34
|
this.navigationStarted(event);
|
|
@@ -73,6 +78,26 @@ class NgDocContentScrollIntent {
|
|
|
73
78
|
navigation.extras.scroll !== 'manual',
|
|
74
79
|
};
|
|
75
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Takes the navigation in progress as the candidate. The providers of a lazy route start the
|
|
83
|
+
* intent while the router loads that route, after the first navigation has started, and its
|
|
84
|
+
* fragment must still be replayed.
|
|
85
|
+
*/
|
|
86
|
+
followCurrentNavigation() {
|
|
87
|
+
const navigation = this.router.currentNavigation();
|
|
88
|
+
if (!navigation)
|
|
89
|
+
return;
|
|
90
|
+
// The router restores the history state of the first navigation, as `restoredState` of its
|
|
91
|
+
// NavigationStart, when that state has a navigation id.
|
|
92
|
+
const state = this.location.getState();
|
|
93
|
+
this.candidate = {
|
|
94
|
+
navigationId: navigation.id,
|
|
95
|
+
eligible: navigation.trigger === 'imperative' &&
|
|
96
|
+
navigation.previousNavigation === null &&
|
|
97
|
+
!state?.navigationId &&
|
|
98
|
+
navigation.extras.scroll !== 'manual',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
76
101
|
navigationEnded(event) {
|
|
77
102
|
const candidate = this.candidate;
|
|
78
103
|
this.candidate = undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-doc-app-services-content-scroll-intent.mjs","sources":["../../../../libs/app/services/content-scroll-intent/content-scroll-intent.service.ts","../../../../libs/app/services/content-scroll-intent/ng-doc-app-services-content-scroll-intent.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport { DestroyRef, inject, PLATFORM_ID, Service, Signal, signal } from '@angular/core';\nimport {\n NavigationCancel,\n NavigationEnd,\n NavigationError,\n NavigationSkipped,\n NavigationStart,\n Router,\n Scroll,\n} from '@angular/router';\nimport { Subscription } from 'rxjs';\n\n/** The fragment of the application's first navigation, kept until a page replays or drops it. */\nexport interface NgDocInitialContentScrollIntent {\n /** The decoded fragment of the navigation. */\n readonly anchor: string;\n /** Id of the navigation that produced the fragment. */\n readonly navigationId: number;\n /** The URL after redirects. */\n readonly url: string;\n}\n\n/** Captures the public initial-navigation facts that are unavailable after component activation. */\n@Service()\nexport class NgDocContentScrollIntent {\n private readonly browser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly router = inject(Router);\n private candidate?: { eligible: boolean; navigationId: number };\n private intent?: NgDocInitialContentScrollIntent;\n private readonly listeners = new Set<\n (intent: NgDocInitialContentScrollIntent | undefined) => void\n >();\n private started = false;\n private successful = false;\n private subscription?: Subscription;\n private readonly intentState = signal<NgDocInitialContentScrollIntent | undefined>(undefined);\n\n /**\n * The pending initial intent as a signal, the same value `subscribe()` listeners receive.\n */\n readonly currentIntent: Signal<NgDocInitialContentScrollIntent | undefined> =\n this.intentState.asReadonly();\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.destroy());\n }\n\n /** Starts following router events. Does nothing on the server or when already started. */\n start(): void {\n if (this.started || !this.browser) return;\n this.started = true;\n this.subscription = this.router.events.subscribe((event) => {\n if (event instanceof NavigationStart) this.navigationStarted(event);\n else if (event instanceof NavigationEnd) this.navigationEnded(event);\n else if (event instanceof Scroll) this.scrolled(event);\n else if (\n event instanceof NavigationCancel ||\n event instanceof NavigationError ||\n event instanceof NavigationSkipped\n ) {\n if (this.candidate && event.id >= this.candidate.navigationId) this.candidate = undefined;\n if (this.intent && event.id >= this.intent.navigationId) this.publish(undefined);\n }\n });\n }\n\n /**\n * Calls the listener with every change of the intent, and at once with the pending one.\n * @param listener - Receives the intent, or `undefined` once it is consumed or cancelled.\n * @returns A function that removes the listener.\n */\n subscribe(listener: (intent: NgDocInitialContentScrollIntent | undefined) => void): () => void {\n this.listeners.add(listener);\n if (this.intent) listener(this.intent);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Consumes the current fallback after its page has either replayed or explicitly yielded it.\n * @param navigationId - The navigation of the intent to consume.\n */\n settle(navigationId: number): void {\n if (this.intent?.navigationId === navigationId) this.publish(undefined);\n }\n\n private navigationStarted(event: NavigationStart): void {\n this.publish(undefined);\n const navigation = this.router.currentNavigation();\n this.candidate = {\n navigationId: event.id,\n eligible:\n !this.successful &&\n event.navigationTrigger === 'imperative' &&\n event.restoredState == null &&\n navigation?.id === event.id &&\n navigation.previousNavigation === null &&\n navigation.extras.scroll !== 'manual',\n };\n }\n\n private navigationEnded(event: NavigationEnd): void {\n const candidate = this.candidate;\n this.candidate = undefined;\n this.successful = true;\n if (!candidate?.eligible || candidate.navigationId !== event.id) return;\n const anchor = this.router.parseUrl(event.urlAfterRedirects).fragment;\n if (anchor) {\n this.publish({ anchor, navigationId: event.id, url: event.urlAfterRedirects });\n }\n }\n\n private scrolled(event: Scroll): void {\n if (this.intent && event.routerEvent.id >= this.intent.navigationId) this.publish(undefined);\n if (this.candidate && event.routerEvent.id >= this.candidate.navigationId) {\n this.candidate = undefined;\n }\n }\n\n private publish(intent: NgDocInitialContentScrollIntent | undefined): void {\n if (this.intent === intent) return;\n this.intent = intent;\n this.intentState.set(intent);\n for (const listener of [...this.listeners]) listener(intent);\n }\n\n private destroy(): void {\n this.subscription?.unsubscribe();\n this.subscription = undefined;\n this.candidate = undefined;\n this.intent = undefined;\n this.intentState.set(undefined);\n this.listeners.clear();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":";;;;;AAuBA;MAEa,wBAAwB,CAAA;AAmBnC,IAAA,WAAA,GAAA;QAlBiB,IAAA,CAAA,OAAO,GAAG,iBAAiB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AAChD,QAAA,IAAA,CAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AAGvB,QAAA,IAAA,CAAA,SAAS,GAAG,IAAI,GAAG,EAEjC;QACK,IAAA,CAAA,OAAO,GAAG,KAAK;QACf,IAAA,CAAA,UAAU,GAAG,KAAK;QAET,IAAA,CAAA,WAAW,GAAG,MAAM,CAA8C,SAAS;wFAAC;AAE7F;;AAEG;AACM,QAAA,IAAA,CAAA,aAAa,GACpB,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE;AAG7B,QAAA,MAAM,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;IACpD;;IAGA,KAAK,GAAA;AACH,QAAA,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE;AACnC,QAAA,IAAI,CAAC,OAAO,GAAG,IAAI;AACnB,QAAA,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,KAAK,KAAI;YACzD,IAAI,KAAK,YAAY,eAAe;AAAE,gBAAA,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC;iBAC9D,IAAI,KAAK,YAAY,aAAa;AAAE,gBAAA,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC;iBAC/D,IAAI,KAAK,YAAY,MAAM;AAAE,gBAAA,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;iBACjD,IACH,KAAK,YAAY,gBAAgB;AACjC,gBAAA,KAAK,YAAY,eAAe;gBAChC,KAAK,YAAY,iBAAiB,EAClC;AACA,gBAAA,IAAI,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY;AAAE,oBAAA,IAAI,CAAC,SAAS,GAAG,SAAS;AACzF,gBAAA,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY;AAAE,oBAAA,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;YAClF;AACF,QAAA,CAAC,CAAC;IACJ;AAEA;;;;AAIG;AACH,IAAA,SAAS,CAAC,QAAuE,EAAA;AAC/E,QAAA,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC5B,IAAI,IAAI,CAAC,MAAM;AAAE,YAAA,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;QACtC,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC9C;AAEA;;;AAGG;AACH,IAAA,MAAM,CAAC,YAAoB,EAAA;AACzB,QAAA,IAAI,IAAI,CAAC,MAAM,EAAE,YAAY,KAAK,YAAY;AAAE,YAAA,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;IACzE;AAEQ,IAAA,iBAAiB,CAAC,KAAsB,EAAA;AAC9C,QAAA,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE;QAClD,IAAI,CAAC,SAAS,GAAG;YACf,YAAY,EAAE,KAAK,CAAC,EAAE;AACtB,YAAA,QAAQ,EACN,CAAC,IAAI,CAAC,UAAU;gBAChB,KAAK,CAAC,iBAAiB,KAAK,YAAY;gBACxC,KAAK,CAAC,aAAa,IAAI,IAAI;AAC3B,gBAAA,UAAU,EAAE,EAAE,KAAK,KAAK,CAAC,EAAE;gBAC3B,UAAU,CAAC,kBAAkB,KAAK,IAAI;AACtC,gBAAA,UAAU,CAAC,MAAM,CAAC,MAAM,KAAK,QAAQ;SACxC;IACH;AAEQ,IAAA,eAAe,CAAC,KAAoB,EAAA;AAC1C,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS;AAChC,QAAA,IAAI,CAAC,SAAS,GAAG,SAAS;AAC1B,QAAA,IAAI,CAAC,UAAU,GAAG,IAAI;QACtB,IAAI,CAAC,SAAS,EAAE,QAAQ,IAAI,SAAS,CAAC,YAAY,KAAK,KAAK,CAAC,EAAE;YAAE;AACjE,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,QAAQ;QACrE,IAAI,MAAM,EAAE;AACV,YAAA,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAChF;IACF;AAEQ,IAAA,QAAQ,CAAC,KAAa,EAAA;AAC5B,QAAA,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,YAAY;AAAE,YAAA,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC;AAC5F,QAAA,IAAI,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE;AACzE,YAAA,IAAI,CAAC,SAAS,GAAG,SAAS;QAC5B;IACF;AAEQ,IAAA,OAAO,CAAC,MAAmD,EAAA;AACjE,QAAA,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM;YAAE;AAC5B,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;AACpB,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC;QAC5B,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC;YAAE,QAAQ,CAAC,MAAM,CAAC;IAC9D;IAEQ,OAAO,GAAA;AACb,QAAA,IAAI,CAAC,YAAY,EAAE,WAAW,EAAE;AAChC,QAAA,IAAI,CAAC,YAAY,GAAG,SAAS;AAC7B,QAAA,IAAI,CAAC,SAAS,GAAG,SAAS;AAC1B,QAAA,IAAI,CAAC,MAAM,GAAG,SAAS;AACvB,QAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC;AAC/B,QAAA,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE;IACxB;8GA5GW,wBAAwB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,OAAA,EAAA,CAAA,CAAA;+GAAxB,wBAAwB,EAAA,CAAA,CAAA;;2FAAxB,wBAAwB,EAAA,UAAA,EAAA,CAAA;kBADpC;;;ACxBD;;AAEG;;;;"}
|
|
1
|
+
{"version":3,"file":"ng-doc-app-services-content-scroll-intent.mjs","sources":["../../../../libs/app/services/content-scroll-intent/content-scroll-intent.service.ts","../../../../libs/app/services/content-scroll-intent/ng-doc-app-services-content-scroll-intent.ts"],"sourcesContent":["import { isPlatformBrowser, Location } from '@angular/common';\nimport { DestroyRef, inject, PLATFORM_ID, Service, Signal, signal } from '@angular/core';\nimport {\n NavigationCancel,\n NavigationEnd,\n NavigationError,\n NavigationSkipped,\n NavigationStart,\n Router,\n Scroll,\n} from '@angular/router';\nimport { Subscription } from 'rxjs';\n\n/** The fragment of the application's first navigation, kept until a page replays or drops it. */\nexport interface NgDocInitialContentScrollIntent {\n /** The decoded fragment of the navigation. */\n readonly anchor: string;\n /** Id of the navigation that produced the fragment. */\n readonly navigationId: number;\n /** The URL after redirects. */\n readonly url: string;\n}\n\n/** Captures the public initial-navigation facts that are unavailable after component activation. */\n@Service()\nexport class NgDocContentScrollIntent {\n private readonly browser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly router = inject(Router);\n private readonly location = inject(Location);\n private candidate?: { eligible: boolean; navigationId: number };\n private intent?: NgDocInitialContentScrollIntent;\n private readonly listeners = new Set<\n (intent: NgDocInitialContentScrollIntent | undefined) => void\n >();\n private started = false;\n private successful = false;\n private subscription?: Subscription;\n private readonly intentState = signal<NgDocInitialContentScrollIntent | undefined>(undefined);\n\n /**\n * The pending initial intent as a signal, the same value `subscribe()` listeners receive.\n */\n readonly currentIntent: Signal<NgDocInitialContentScrollIntent | undefined> =\n this.intentState.asReadonly();\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.destroy());\n }\n\n /**\n * Starts following router events. Does nothing on the server or when already started. Started\n * during a navigation, it follows that navigation too.\n */\n start(): void {\n if (this.started || !this.browser) return;\n this.started = true;\n this.followCurrentNavigation();\n this.subscription = this.router.events.subscribe((event) => {\n if (event instanceof NavigationStart) this.navigationStarted(event);\n else if (event instanceof NavigationEnd) this.navigationEnded(event);\n else if (event instanceof Scroll) this.scrolled(event);\n else if (\n event instanceof NavigationCancel ||\n event instanceof NavigationError ||\n event instanceof NavigationSkipped\n ) {\n if (this.candidate && event.id >= this.candidate.navigationId) this.candidate = undefined;\n if (this.intent && event.id >= this.intent.navigationId) this.publish(undefined);\n }\n });\n }\n\n /**\n * Calls the listener with every change of the intent, and at once with the pending one.\n * @param listener - Receives the intent, or `undefined` once it is consumed or cancelled.\n * @returns A function that removes the listener.\n */\n subscribe(listener: (intent: NgDocInitialContentScrollIntent | undefined) => void): () => void {\n this.listeners.add(listener);\n if (this.intent) listener(this.intent);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * Consumes the current fallback after its page has either replayed or explicitly yielded it.\n * @param navigationId - The navigation of the intent to consume.\n */\n settle(navigationId: number): void {\n if (this.intent?.navigationId === navigationId) this.publish(undefined);\n }\n\n private navigationStarted(event: NavigationStart): void {\n this.publish(undefined);\n const navigation = this.router.currentNavigation();\n this.candidate = {\n navigationId: event.id,\n eligible:\n !this.successful &&\n event.navigationTrigger === 'imperative' &&\n event.restoredState == null &&\n navigation?.id === event.id &&\n navigation.previousNavigation === null &&\n navigation.extras.scroll !== 'manual',\n };\n }\n\n /**\n * Takes the navigation in progress as the candidate. The providers of a lazy route start the\n * intent while the router loads that route, after the first navigation has started, and its\n * fragment must still be replayed.\n */\n private followCurrentNavigation(): void {\n const navigation = this.router.currentNavigation();\n\n if (!navigation) return;\n\n // The router restores the history state of the first navigation, as `restoredState` of its\n // NavigationStart, when that state has a navigation id.\n const state = this.location.getState() as { navigationId?: unknown } | null;\n\n this.candidate = {\n navigationId: navigation.id,\n eligible:\n navigation.trigger === 'imperative' &&\n navigation.previousNavigation === null &&\n !state?.navigationId &&\n navigation.extras.scroll !== 'manual',\n };\n }\n\n private navigationEnded(event: NavigationEnd): void {\n const candidate = this.candidate;\n this.candidate = undefined;\n this.successful = true;\n if (!candidate?.eligible || candidate.navigationId !== event.id) return;\n const anchor = this.router.parseUrl(event.urlAfterRedirects).fragment;\n if (anchor) {\n this.publish({ anchor, navigationId: event.id, url: event.urlAfterRedirects });\n }\n }\n\n private scrolled(event: Scroll): void {\n if (this.intent && event.routerEvent.id >= this.intent.navigationId) this.publish(undefined);\n if (this.candidate && event.routerEvent.id >= this.candidate.navigationId) {\n this.candidate = undefined;\n }\n }\n\n private publish(intent: NgDocInitialContentScrollIntent | undefined): void {\n if (this.intent === intent) return;\n this.intent = intent;\n this.intentState.set(intent);\n for (const listener of [...this.listeners]) listener(intent);\n }\n\n private destroy(): void {\n this.subscription?.unsubscribe();\n this.subscription = undefined;\n this.candidate = undefined;\n this.intent = undefined;\n this.intentState.set(undefined);\n this.listeners.clear();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -2,12 +2,11 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { signal, inject, isDevMode, Service } from '@angular/core';
|
|
3
3
|
import { NG_DOC_SHIKI_THEME } from '@ng-doc/app/tokens';
|
|
4
4
|
import { ngDocSyntaxTheme } from '@ng-doc/core/constants/syntax-theme';
|
|
5
|
-
import { createHighlighterCore } from 'shiki/core';
|
|
6
|
-
import { createOnigurumaEngine } from 'shiki/engine/oniguruma';
|
|
7
|
-
import getWasm from 'shiki/wasm';
|
|
8
5
|
|
|
9
6
|
/**
|
|
10
|
-
* Highlights code with Shiki. `provideNgDocApp`
|
|
7
|
+
* Highlights code with Shiki. `provideNgDocApp` starts its initialization where it is provided (the
|
|
8
|
+
* application, or the route that holds the documentation), and the server render waits for it.
|
|
9
|
+
* Shiki is loaded on demand: `initialize()` imports it.
|
|
11
10
|
*/
|
|
12
11
|
class NgDocHighlighterService {
|
|
13
12
|
constructor() {
|
|
@@ -76,6 +75,10 @@ class NgDocHighlighterService {
|
|
|
76
75
|
this.highlighter = undefined;
|
|
77
76
|
}
|
|
78
77
|
static async create(themes = [], langs = []) {
|
|
78
|
+
const [{ createHighlighterCore }, { createOnigurumaEngine }] = await Promise.all([
|
|
79
|
+
import('shiki/core'),
|
|
80
|
+
import('shiki/engine/oniguruma'),
|
|
81
|
+
]);
|
|
79
82
|
return createHighlighterCore({
|
|
80
83
|
themes: [
|
|
81
84
|
import('shiki/themes/github-light.mjs'),
|
|
@@ -88,7 +91,7 @@ class NgDocHighlighterService {
|
|
|
88
91
|
langs: [import('shiki/langs/angular-html.mjs'), ...langs],
|
|
89
92
|
// The Oniguruma engine, as at build time, so that the browser tokenizes code as the code
|
|
90
93
|
// blocks were tokenized.
|
|
91
|
-
engine: createOnigurumaEngine(
|
|
94
|
+
engine: createOnigurumaEngine(import('shiki/wasm')),
|
|
92
95
|
});
|
|
93
96
|
}
|
|
94
97
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ng-doc-app-services-highlighter.mjs","sources":["../../../../libs/app/services/highlighter/highlighter.service.ts","../../../../libs/app/services/highlighter/ng-doc-app-services-highlighter.ts"],"sourcesContent":["import { inject, isDevMode, OnDestroy, Service, Signal, signal } from '@angular/core';\nimport { NG_DOC_SHIKI_THEME } from '@ng-doc/app/tokens';\nimport { ngDocSyntaxTheme } from '@ng-doc/core/constants/syntax-theme';\
|
|
1
|
+
{"version":3,"file":"ng-doc-app-services-highlighter.mjs","sources":["../../../../libs/app/services/highlighter/highlighter.service.ts","../../../../libs/app/services/highlighter/ng-doc-app-services-highlighter.ts"],"sourcesContent":["import { inject, isDevMode, OnDestroy, Service, Signal, signal } from '@angular/core';\nimport { NG_DOC_SHIKI_THEME } from '@ng-doc/app/tokens';\nimport { ngDocSyntaxTheme } from '@ng-doc/core/constants/syntax-theme';\n// Types only: Shiki, its Oniguruma engine and the WebAssembly binary load with the first\n// `initialize()`, so an application that imports NgDoc only in a lazy route keeps them out of its\n// initial bundle.\nimport type { HighlighterCore, LanguageInput, ThemeInput } from 'shiki/core';\n\n/** Shiki configuration of the code highlighter. */\nexport interface NgDocHighlighterConfig {\n /**\n * Themes sources.\n */\n themes?: ThemeInput[];\n /**\n * Shiki languages to load besides `angular-html`, the language of playground code, and the\n * languages it embeds. A language named like one of them replaces it, so register here the\n * languages of `shiki.langs` in `ng-doc.config.ts` that playground code uses, for example a newer\n * Angular template grammar.\n */\n langs?: LanguageInput[];\n /**\n * Has no effect, and is reported in development mode.\n *\n * The theme names come from `shiki.themes` in `ng-doc.config.ts`: NgDoc highlights code blocks\n * with them when it builds the site, and passes them to the application (`NG_DOC_SHIKI_THEME`),\n * so the code that the browser highlights matches the code blocks.\n * @deprecated Set the theme names in `shiki.themes` of `ng-doc.config.ts` instead, and load\n * other themes with `themes`. This field will be removed in a future major release.\n */\n theme?: {\n light: string;\n dark: string;\n };\n}\n\n/**\n * Highlights code with Shiki. `provideNgDocApp` starts its initialization where it is provided (the\n * application, or the route that holds the documentation), and the server render waits for it.\n * Shiki is loaded on demand: `initialize()` imports it.\n */\n@Service()\nexport class NgDocHighlighterService implements OnDestroy {\n private static defaultInitialization?: Promise<HighlighterCore>;\n\n private highlighter?: HighlighterCore;\n private initialization?: Promise<void>;\n private ownsHighlighter = false;\n private destroyed = false;\n private readonly readyState = signal(false);\n\n protected readonly theme = inject(NG_DOC_SHIKI_THEME);\n\n /**\n * Whether `highlight()` can highlight: `true` once `initialize()` has finished, and\n * `false` again after the service is destroyed.\n */\n readonly ready: Signal<boolean> = this.readyState.asReadonly();\n\n /**\n * Loads Shiki with the built-in themes (`github-light`, `ayu-dark` and NgDoc's `css-variables`)\n * and the given ones, and the given languages. Concurrent and repeated calls share one\n * initialization; a failed one can be retried.\n * @param config - Custom Shiki themes and languages to load.\n */\n initialize(config?: NgDocHighlighterConfig): Promise<void> {\n if (this.destroyed) {\n return Promise.reject(new Error('NgDoc highlighter has been destroyed.'));\n }\n if (this.initialization) {\n return this.initialization;\n }\n if (config?.theme && isDevMode()) {\n console.warn(\n '[NgDoc] `shiki.theme` of `provideNgDocApp` has no effect. Set the theme names in ' +\n '`shiki.themes` of `ng-doc.config.ts`: NgDoc uses them for the code blocks and the ' +\n 'code that the browser highlights.',\n );\n }\n\n // Only immutable built-in themes and languages are shared across SSR applications. Custom\n // ones may reuse names with other definitions, or be asynchronous getters.\n this.ownsHighlighter = Boolean(config?.themes?.length || config?.langs?.length);\n const creation = this.ownsHighlighter\n ? NgDocHighlighterService.create(config?.themes, config?.langs)\n : (NgDocHighlighterService.defaultInitialization ??= NgDocHighlighterService.create().catch(\n (error: unknown) => {\n NgDocHighlighterService.defaultInitialization = undefined;\n throw error;\n },\n ));\n this.initialization = creation\n .then((highlighter) => {\n if (this.destroyed) {\n if (this.ownsHighlighter) {\n highlighter.dispose();\n }\n return;\n }\n this.highlighter = highlighter;\n this.readyState.set(true);\n })\n .catch((error: unknown) => {\n this.initialization = undefined;\n throw error;\n });\n return this.initialization;\n }\n\n /** Disposes the highlighter this service created for custom themes or languages. */\n ngOnDestroy(): void {\n this.destroyed = true;\n this.readyState.set(false);\n if (this.ownsHighlighter) {\n this.highlighter?.dispose();\n }\n this.highlighter = undefined;\n }\n\n private static async create(\n themes: ThemeInput[] = [],\n langs: LanguageInput[] = [],\n ): Promise<HighlighterCore> {\n const [{ createHighlighterCore }, { createOnigurumaEngine }] = await Promise.all([\n import('shiki/core'),\n import('shiki/engine/oniguruma'),\n ]);\n\n return createHighlighterCore({\n themes: [\n import('shiki/themes/github-light.mjs'),\n import('shiki/themes/ayu-dark.mjs'),\n // A new copy: Shiki changes the theme object it loads.\n ngDocSyntaxTheme(),\n ...themes,\n ],\n // Later registrations of a name replace earlier ones, so the given languages win.\n langs: [import('shiki/langs/angular-html.mjs'), ...langs],\n // The Oniguruma engine, as at build time, so that the browser tokenizes code as the code\n // blocks were tokenized.\n engine: createOnigurumaEngine(import('shiki/wasm')),\n });\n }\n\n /**\n * Returns the code as highlighted HTML, or an empty string before `initialize()` has\n * finished. It reads `ready()`, so a `computed` over it updates once Shiki is loaded.\n * @param code - Angular HTML to highlight.\n */\n highlight(code: string): string {\n if (!this.readyState()) return '';\n\n return (\n this.highlighter?.codeToHtml(code, {\n lang: 'angular-html',\n themes: {\n light: this.theme.light || 'github-light',\n dark: this.theme.dark || 'ayu-dark',\n },\n }) ?? ''\n );\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -50,6 +50,10 @@ const OWN_KEYS = [
|
|
|
50
50
|
* the choice is saved in the browser. A site turns them off by default with the shortcuts option
|
|
51
51
|
* of provideNgDocApp. They never run while the reader types in a field or holds Command, Control
|
|
52
52
|
* or Alt. Chords such as Command+K (Control+K) always work.
|
|
53
|
+
*
|
|
54
|
+
* Shortcuts run only while the documentation layout is shown: `NgDocRootComponent` turns them on
|
|
55
|
+
* with `listen()`, so the other pages of an application that shows its documentation under a
|
|
56
|
+
* route keep their own keys.
|
|
53
57
|
*/
|
|
54
58
|
class NgDocShortcutsService {
|
|
55
59
|
constructor() {
|
|
@@ -61,6 +65,10 @@ class NgDocShortcutsService {
|
|
|
61
65
|
this.userChoice = signal(undefined, /* @ts-ignore */
|
|
62
66
|
...(ngDevMode ? [{ debugName: "userChoice" }] : /* istanbul ignore next */ []));
|
|
63
67
|
this.shortcuts = [];
|
|
68
|
+
this.listener = (event) => this.onKeydown(event);
|
|
69
|
+
/** The layouts that listen for the shortcuts; the document listener is on while there are any. */
|
|
70
|
+
this.listeners = 0;
|
|
71
|
+
this.destroyed = false;
|
|
64
72
|
/**
|
|
65
73
|
* Whether single-key shortcuts are on: the reader's saved choice, or the site's default.
|
|
66
74
|
* Components hide the hints of single-key shortcuts while it is false.
|
|
@@ -74,10 +82,36 @@ class NgDocShortcutsService {
|
|
|
74
82
|
// The saved choice is read after the first render, so a hydrated page first renders exactly
|
|
75
83
|
// what the server rendered (the site's default) and then switches.
|
|
76
84
|
afterNextRender(() => this.userChoice.set(this.readChoice()), { injector: this.injector });
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
inject(DestroyRef).onDestroy(() => {
|
|
86
|
+
this.destroyed = true;
|
|
87
|
+
this.document.removeEventListener('keydown', this.listener);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Runs the shortcuts on key presses until the returned function is called. `NgDocRootComponent`
|
|
92
|
+
* calls it while the documentation layout is shown; a site with a layout of its own calls it
|
|
93
|
+
* the same way. Several callers may listen at once: the shortcuts run until each has stopped.
|
|
94
|
+
* Does nothing on the server.
|
|
95
|
+
* @returns A function that stops listening; later calls do nothing.
|
|
96
|
+
*/
|
|
97
|
+
listen() {
|
|
98
|
+
if (!this.browser || this.destroyed) {
|
|
99
|
+
return () => undefined;
|
|
100
|
+
}
|
|
101
|
+
if (this.listeners++ === 0) {
|
|
102
|
+
// One document listener for every shortcut; change detection runs only when one matches.
|
|
103
|
+
this.ngZone.runOutsideAngular(() => this.document.addEventListener('keydown', this.listener));
|
|
104
|
+
}
|
|
105
|
+
let listening = true;
|
|
106
|
+
return () => {
|
|
107
|
+
if (!listening) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
listening = false;
|
|
111
|
+
if (--this.listeners === 0) {
|
|
112
|
+
this.document.removeEventListener('keydown', this.listener);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
81
115
|
}
|
|
82
116
|
/**
|
|
83
117
|
* Turns single-key shortcuts on or off for this reader and saves the choice in the browser.
|
|
@@ -1 +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
|
+
{"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 * Shortcuts run only while the documentation layout is shown: `NgDocRootComponent` turns them on\n * with `listen()`, so the other pages of an application that shows its documentation under a\n * route keep their own keys.\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 private readonly listener = (event: KeyboardEvent): void => this.onKeydown(event);\n /** The layouts that listen for the shortcuts; the document listener is on while there are any. */\n private listeners = 0;\n private destroyed = false;\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 inject(DestroyRef).onDestroy(() => {\n this.destroyed = true;\n this.document.removeEventListener('keydown', this.listener);\n });\n }\n\n /**\n * Runs the shortcuts on key presses until the returned function is called. `NgDocRootComponent`\n * calls it while the documentation layout is shown; a site with a layout of its own calls it\n * the same way. Several callers may listen at once: the shortcuts run until each has stopped.\n * Does nothing on the server.\n * @returns A function that stops listening; later calls do nothing.\n */\n listen(): () => void {\n if (!this.browser || this.destroyed) {\n return () => undefined;\n }\n\n if (this.listeners++ === 0) {\n // One document listener for every shortcut; change detection runs only when one matches.\n this.ngZone.runOutsideAngular(() => this.document.addEventListener('keydown', this.listener));\n }\n\n let listening = true;\n\n return () => {\n if (!listening) {\n return;\n }\n\n listening = false;\n\n if (--this.listeners === 0) {\n this.document.removeEventListener('keydown', this.listener);\n }\n };\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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"}
|