@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.
Files changed (157) hide show
  1. package/LICENSE +21 -0
  2. package/fesm2022/ng-doc-app-classes-content-controller.mjs +22 -4
  3. package/fesm2022/ng-doc-app-classes-content-controller.mjs.map +1 -1
  4. package/fesm2022/ng-doc-app-classes-default-search-engine.mjs +36 -12
  5. package/fesm2022/ng-doc-app-classes-default-search-engine.mjs.map +1 -1
  6. package/fesm2022/ng-doc-app-classes-root-page.mjs.map +1 -1
  7. package/fesm2022/ng-doc-app-components-api-list.mjs +7 -5
  8. package/fesm2022/ng-doc-app-components-api-list.mjs.map +1 -1
  9. package/fesm2022/ng-doc-app-components-breadcrumb.mjs +5 -3
  10. package/fesm2022/ng-doc-app-components-breadcrumb.mjs.map +1 -1
  11. package/fesm2022/ng-doc-app-components-code.mjs +6 -4
  12. package/fesm2022/ng-doc-app-components-code.mjs.map +1 -1
  13. package/fesm2022/ng-doc-app-components-command-palette.mjs +35 -27
  14. package/fesm2022/ng-doc-app-components-command-palette.mjs.map +1 -1
  15. package/fesm2022/ng-doc-app-components-copy-button.mjs +6 -4
  16. package/fesm2022/ng-doc-app-components-copy-button.mjs.map +1 -1
  17. package/fesm2022/ng-doc-app-components-demo-displayer.mjs +19 -4
  18. package/fesm2022/ng-doc-app-components-demo-displayer.mjs.map +1 -1
  19. package/fesm2022/ng-doc-app-components-demo-frame.mjs +195 -12
  20. package/fesm2022/ng-doc-app-components-demo-frame.mjs.map +1 -1
  21. package/fesm2022/ng-doc-app-components-demo-pane.mjs +2 -2
  22. package/fesm2022/ng-doc-app-components-demo-pane.mjs.map +1 -1
  23. package/fesm2022/ng-doc-app-components-demo.mjs +49 -12
  24. package/fesm2022/ng-doc-app-components-demo.mjs.map +1 -1
  25. package/fesm2022/ng-doc-app-components-fullscreen-button.mjs +5 -3
  26. package/fesm2022/ng-doc-app-components-fullscreen-button.mjs.map +1 -1
  27. package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs +4 -2
  28. package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs.map +1 -1
  29. package/fesm2022/ng-doc-app-components-heading-anchor.mjs +5 -3
  30. package/fesm2022/ng-doc-app-components-heading-anchor.mjs.map +1 -1
  31. package/fesm2022/ng-doc-app-components-image-viewer.mjs +5 -3
  32. package/fesm2022/ng-doc-app-components-image-viewer.mjs.map +1 -1
  33. package/fesm2022/ng-doc-app-components-navbar.mjs +4 -2
  34. package/fesm2022/ng-doc-app-components-navbar.mjs.map +1 -1
  35. package/fesm2022/ng-doc-app-components-page-navigation.mjs +5 -3
  36. package/fesm2022/ng-doc-app-components-page-navigation.mjs.map +1 -1
  37. package/fesm2022/ng-doc-app-components-page-wrapper.mjs +2 -2
  38. package/fesm2022/ng-doc-app-components-page-wrapper.mjs.map +1 -1
  39. package/fesm2022/ng-doc-app-components-page.mjs +83 -16
  40. package/fesm2022/ng-doc-app-components-page.mjs.map +1 -1
  41. package/fesm2022/ng-doc-app-components-playground.mjs +743 -108
  42. package/fesm2022/ng-doc-app-components-playground.mjs.map +1 -1
  43. package/fesm2022/ng-doc-app-components-root.mjs +51 -4
  44. package/fesm2022/ng-doc-app-components-root.mjs.map +1 -1
  45. package/fesm2022/ng-doc-app-components-search-result.mjs +8 -6
  46. package/fesm2022/ng-doc-app-components-search-result.mjs.map +1 -1
  47. package/fesm2022/ng-doc-app-components-search.mjs +7 -5
  48. package/fesm2022/ng-doc-app-components-search.mjs.map +1 -1
  49. package/fesm2022/ng-doc-app-components-sidebar.mjs +4 -3
  50. package/fesm2022/ng-doc-app-components-sidebar.mjs.map +1 -1
  51. package/fesm2022/ng-doc-app-components-theme-toggle.mjs +7 -5
  52. package/fesm2022/ng-doc-app-components-theme-toggle.mjs.map +1 -1
  53. package/fesm2022/ng-doc-app-components-toc.mjs +4 -2
  54. package/fesm2022/ng-doc-app-components-toc.mjs.map +1 -1
  55. package/fesm2022/ng-doc-app-constants.mjs +11 -1
  56. package/fesm2022/ng-doc-app-constants.mjs.map +1 -1
  57. package/fesm2022/ng-doc-app-demo-app.mjs +618 -15
  58. package/fesm2022/ng-doc-app-demo-app.mjs.map +1 -1
  59. package/fesm2022/ng-doc-app-helpers.mjs +125 -6
  60. package/fesm2022/ng-doc-app-helpers.mjs.map +1 -1
  61. package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs +29 -18
  62. package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs.map +1 -1
  63. package/fesm2022/ng-doc-app-providers-texts.mjs +53 -0
  64. package/fesm2022/ng-doc-app-providers-texts.mjs.map +1 -0
  65. package/fesm2022/ng-doc-app-providers.mjs +1 -0
  66. package/fesm2022/ng-doc-app-providers.mjs.map +1 -1
  67. package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs +27 -2
  68. package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs.map +1 -1
  69. package/fesm2022/ng-doc-app-services-highlighter.mjs +8 -5
  70. package/fesm2022/ng-doc-app-services-highlighter.mjs.map +1 -1
  71. package/fesm2022/ng-doc-app-services-shortcuts.mjs +38 -4
  72. package/fesm2022/ng-doc-app-services-shortcuts.mjs.map +1 -1
  73. package/fesm2022/ng-doc-app-texts-ar.mjs +238 -0
  74. package/fesm2022/ng-doc-app-texts-ar.mjs.map +1 -0
  75. package/fesm2022/ng-doc-app-texts-de.mjs +205 -0
  76. package/fesm2022/ng-doc-app-texts-de.mjs.map +1 -0
  77. package/fesm2022/ng-doc-app-texts-es.mjs +205 -0
  78. package/fesm2022/ng-doc-app-texts-es.mjs.map +1 -0
  79. package/fesm2022/ng-doc-app-texts-fr.mjs +205 -0
  80. package/fesm2022/ng-doc-app-texts-fr.mjs.map +1 -0
  81. package/fesm2022/ng-doc-app-texts-hi.mjs +219 -0
  82. package/fesm2022/ng-doc-app-texts-hi.mjs.map +1 -0
  83. package/fesm2022/ng-doc-app-texts-ja.mjs +205 -0
  84. package/fesm2022/ng-doc-app-texts-ja.mjs.map +1 -0
  85. package/fesm2022/ng-doc-app-texts-ko.mjs +205 -0
  86. package/fesm2022/ng-doc-app-texts-ko.mjs.map +1 -0
  87. package/fesm2022/ng-doc-app-texts-pt-br.mjs +205 -0
  88. package/fesm2022/ng-doc-app-texts-pt-br.mjs.map +1 -0
  89. package/fesm2022/ng-doc-app-texts-ru.mjs +231 -0
  90. package/fesm2022/ng-doc-app-texts-ru.mjs.map +1 -0
  91. package/fesm2022/ng-doc-app-texts-uk.mjs +231 -0
  92. package/fesm2022/ng-doc-app-texts-uk.mjs.map +1 -0
  93. package/fesm2022/ng-doc-app-texts-zh.mjs +205 -0
  94. package/fesm2022/ng-doc-app-texts-zh.mjs.map +1 -0
  95. package/fesm2022/ng-doc-app-tokens.mjs +214 -1
  96. package/fesm2022/ng-doc-app-tokens.mjs.map +1 -1
  97. package/fesm2022/ng-doc-app-type-controls-json-control.mjs +90 -0
  98. package/fesm2022/ng-doc-app-type-controls-json-control.mjs.map +1 -0
  99. package/fesm2022/ng-doc-app-type-controls-list-control.mjs +792 -0
  100. package/fesm2022/ng-doc-app-type-controls-list-control.mjs.map +1 -0
  101. package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs +5 -3
  102. package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs.map +1 -1
  103. package/fesm2022/ng-doc-app-type-controls.mjs +2 -0
  104. package/fesm2022/ng-doc-app-type-controls.mjs.map +1 -1
  105. package/package.json +59 -3
  106. package/styles/global.css +1 -0
  107. package/styles/main.css +1 -0
  108. package/types/ng-doc-app-classes-default-search-engine.d.ts +11 -0
  109. package/types/ng-doc-app-classes-root-page.d.ts +5 -0
  110. package/types/ng-doc-app-components-api-list.d.ts +3 -1
  111. package/types/ng-doc-app-components-breadcrumb.d.ts +2 -1
  112. package/types/ng-doc-app-components-code.d.ts +2 -0
  113. package/types/ng-doc-app-components-command-palette.d.ts +3 -1
  114. package/types/ng-doc-app-components-copy-button.d.ts +3 -1
  115. package/types/ng-doc-app-components-demo-displayer.d.ts +13 -1
  116. package/types/ng-doc-app-components-demo-frame.d.ts +62 -3
  117. package/types/ng-doc-app-components-demo.d.ts +27 -8
  118. package/types/ng-doc-app-components-fullscreen-button.d.ts +2 -0
  119. package/types/ng-doc-app-components-fullscreen-toggle.d.ts +2 -0
  120. package/types/ng-doc-app-components-heading-anchor.d.ts +2 -0
  121. package/types/ng-doc-app-components-image-viewer.d.ts +2 -0
  122. package/types/ng-doc-app-components-navbar.d.ts +2 -0
  123. package/types/ng-doc-app-components-page-navigation.d.ts +2 -1
  124. package/types/ng-doc-app-components-page.d.ts +10 -1
  125. package/types/ng-doc-app-components-playground.d.ts +241 -44
  126. package/types/ng-doc-app-components-root.d.ts +18 -0
  127. package/types/ng-doc-app-components-search-result.d.ts +2 -0
  128. package/types/ng-doc-app-components-search.d.ts +2 -0
  129. package/types/ng-doc-app-components-sidebar.d.ts +2 -1
  130. package/types/ng-doc-app-components-theme-toggle.d.ts +4 -1
  131. package/types/ng-doc-app-components-toc.d.ts +2 -1
  132. package/types/ng-doc-app-constants.d.ts +11 -1
  133. package/types/ng-doc-app-demo-app.d.ts +211 -3
  134. package/types/ng-doc-app-helpers.d.ts +56 -3
  135. package/types/ng-doc-app-interfaces.d.ts +621 -3
  136. package/types/ng-doc-app-providers-ng-doc-app.d.ts +6 -0
  137. package/types/ng-doc-app-providers-texts.d.ts +28 -0
  138. package/types/ng-doc-app-providers.d.ts +1 -0
  139. package/types/ng-doc-app-services-content-scroll-intent.d.ts +11 -1
  140. package/types/ng-doc-app-services-highlighter.d.ts +3 -1
  141. package/types/ng-doc-app-services-shortcuts.d.ts +16 -0
  142. package/types/ng-doc-app-texts-ar.d.ts +13 -0
  143. package/types/ng-doc-app-texts-de.d.ts +12 -0
  144. package/types/ng-doc-app-texts-es.d.ts +12 -0
  145. package/types/ng-doc-app-texts-fr.d.ts +12 -0
  146. package/types/ng-doc-app-texts-hi.d.ts +12 -0
  147. package/types/ng-doc-app-texts-ja.d.ts +12 -0
  148. package/types/ng-doc-app-texts-ko.d.ts +12 -0
  149. package/types/ng-doc-app-texts-pt-br.d.ts +12 -0
  150. package/types/ng-doc-app-texts-ru.d.ts +12 -0
  151. package/types/ng-doc-app-texts-uk.d.ts +12 -0
  152. package/types/ng-doc-app-texts-zh.d.ts +12 -0
  153. package/types/ng-doc-app-tokens.d.ts +21 -2
  154. package/types/ng-doc-app-type-controls-json-control.d.ts +38 -0
  155. package/types/ng-doc-app-type-controls-list-control.d.ts +313 -0
  156. package/types/ng-doc-app-type-controls-type-alias-control.d.ts +2 -1
  157. package/types/ng-doc-app-type-controls.d.ts +2 -0
@@ -1,6 +1,6 @@
1
1
  import { isPlatformBrowser, NgComponentOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { inject, PLATFORM_ID, DOCUMENT, DestroyRef, ElementRef, ChangeDetectionStrategy, Component, provideZoneChangeDetection, provideZonelessChangeDetection } from '@angular/core';
3
+ import { inject, PLATFORM_ID, DOCUMENT, DestroyRef, ElementRef, ChangeDetectionStrategy, ViewEncapsulation, Component, provideZoneChangeDetection, provideZonelessChangeDetection } from '@angular/core';
4
4
  import { RouterOutlet, ActivatedRoute, provideRouter } from '@angular/router';
5
5
  import { NG_DOC_STORE_THEME_KEY } from '@ng-doc/app/constants';
6
6
  import { provideClientHydration, withNoIncrementalHydration } from '@angular/platform-browser';
@@ -26,12 +26,82 @@ function ɵisNgDocDemoSizeMessage(value) {
26
26
  Number.isFinite(message.height) &&
27
27
  message.height >= 0);
28
28
  }
29
+ /**
30
+ * Whether a message is a background message of the demo application.
31
+ * @param value - The data of a `message` event.
32
+ * @internal
33
+ */
34
+ function ɵisNgDocDemoBackgroundMessage(value) {
35
+ const message = value;
36
+ return (typeof message === 'object' &&
37
+ message !== null &&
38
+ message.source === ɵNG_DOC_DEMO_MESSAGE_SOURCE &&
39
+ message.version === 1 &&
40
+ message.type === 'background' &&
41
+ typeof message.color === 'string' &&
42
+ message.color.length > 0 &&
43
+ message.color.length <= 200 &&
44
+ (message.dots === undefined ||
45
+ (typeof message.dots === 'string' && message.dots.length <= 200)) &&
46
+ (message.scheme === undefined ||
47
+ (typeof message.scheme === 'string' && message.scheme.length <= 50)));
48
+ }
49
+
50
+ /** The fragment parameter that sets the theme of a demo page; an input name never starts with `~`. */
51
+ const THEME = '~theme';
52
+ /**
53
+ * The theme that the URL fragment of a demo page sets, or `undefined` when it sets none and the
54
+ * page follows the theme of the site.
55
+ * @param fragment - The fragment, with or without the leading `#`.
56
+ * @internal
57
+ */
58
+ function ɵdemoThemeFromFragment(fragment) {
59
+ for (const parameter of fragment.replace(/^#/, '').split('&')) {
60
+ if (!parameter.startsWith(`${THEME}=`))
61
+ continue;
62
+ try {
63
+ return decodeURIComponent(parameter.slice(THEME.length + 1)) || undefined;
64
+ }
65
+ catch {
66
+ return undefined;
67
+ }
68
+ }
69
+ return undefined;
70
+ }
71
+ /**
72
+ * A fragment (without `#`) with the theme parameter set, after the other parameters, or without it
73
+ * when there is no theme. `undefined` when nothing is left.
74
+ * @param fragment - The fragment without the theme, or none.
75
+ * @param theme - The theme of the demo page, or none to follow the site.
76
+ * @internal
77
+ */
78
+ function ɵwithDemoTheme(fragment, theme) {
79
+ const parameters = (fragment ?? '')
80
+ .split('&')
81
+ .filter((parameter) => parameter && !parameter.startsWith(`${THEME}=`));
82
+ if (theme)
83
+ parameters.push(`${THEME}=${encodeURIComponent(theme)}`);
84
+ return parameters.length ? parameters.join('&') : undefined;
85
+ }
86
+ /**
87
+ * Whether a parameter of a fragment is the theme of the demo page, which the state of a
88
+ * playground leaves to the demo application.
89
+ * @param name - The decoded name of the parameter.
90
+ * @internal
91
+ */
92
+ function ɵisDemoThemeParameter(name) {
93
+ return name === THEME;
94
+ }
29
95
 
30
96
  /**
31
97
  * The root of the demo application, which shows one demo per page with only Angular and that
32
98
  * demo loaded. In an iframe it reports the height of its content to the page that embeds it (a
33
99
  * `ResizeObserver` on its border box), and it follows the theme the reader picks on the
34
- * documentation site, which stores it in `localStorage`.
100
+ * documentation site, which stores it in `localStorage`, unless its URL fragment sets a theme
101
+ * (`~theme=dark`, the `theme` option of a demo or a playground, or the theme button beside it).
102
+ * In an iframe its page is transparent, so the canvas of the documentation page shows through, and
103
+ * it reports its theme's background, dots and color scheme whenever they change, so that the
104
+ * embedding page can paint the canvas in the demo's own theme.
35
105
  */
36
106
  class NgDocDemoAppComponent {
37
107
  constructor() {
@@ -42,11 +112,45 @@ class NgDocDemoAppComponent {
42
112
  const destroyRef = inject(DestroyRef);
43
113
  if (!window)
44
114
  return;
45
- if (window.parent !== window) {
115
+ const embedded = window.parent !== window;
116
+ let reportedBackground = '';
117
+ // The page's background: the body's, or the document's when the body has none. Reported only
118
+ // when it changes, and once the page has one (its styles may still be loading).
119
+ const reportBackground = () => {
120
+ if (!embedded)
121
+ return;
122
+ // The theme's background and dots, from its variables: the page itself is transparent in
123
+ // an iframe, so its computed background is not the theme's.
124
+ const style = window.getComputedStyle(document.documentElement);
125
+ const color = style.getPropertyValue('--ng-doc-background').trim();
126
+ const dots = style.getPropertyValue('--ng-doc-base-5').trim();
127
+ // A transparent iframe shows its page only while the iframe element has the page's color
128
+ // scheme; otherwise the browser paints an opaque backdrop behind it.
129
+ const scheme = style.colorScheme && style.colorScheme !== 'normal' ? style.colorScheme : '';
130
+ const key = `${color}|${dots}|${scheme}`;
131
+ if (!color || key === reportedBackground)
132
+ return;
133
+ reportedBackground = key;
134
+ const message = {
135
+ source: ɵNG_DOC_DEMO_MESSAGE_SOURCE,
136
+ version: 1,
137
+ type: 'background',
138
+ color,
139
+ ...(dots ? { dots } : {}),
140
+ ...(scheme ? { scheme } : {}),
141
+ };
142
+ window.parent.postMessage(message, window.location.origin);
143
+ };
144
+ if (embedded) {
145
+ // The page is transparent in an iframe, so the canvas of the documentation page, dots and
146
+ // all, shows through around and behind the demo.
147
+ document.documentElement.classList.add('ng-doc-demo-embedded');
46
148
  const host = inject(ElementRef).nativeElement;
47
149
  let reported = -1;
48
150
  const observer = new ResizeObserver(() => {
49
151
  const height = Math.ceil(host.getBoundingClientRect().height);
152
+ // Styles that load after the first report change the background too.
153
+ reportBackground();
50
154
  if (height === reported)
51
155
  return;
52
156
  reported = height;
@@ -62,28 +166,55 @@ class NgDocDemoAppComponent {
62
166
  observer.observe(host, { box: 'border-box' });
63
167
  destroyRef.onDestroy(() => observer.disconnect());
64
168
  }
65
- // The documentation page and the demo page share the origin, so a theme the reader picks
66
- // reaches this page as a storage event. An empty id is the light theme, as in the restore
67
- // script that applies the stored theme before the application starts.
68
- const onStorage = (event) => {
69
- if (event.key !== NG_DOC_STORE_THEME_KEY || event.newValue === null)
70
- return;
71
- if (event.newValue) {
72
- document.documentElement.setAttribute('data-theme', event.newValue);
169
+ // An empty id is the light theme, as in the restore script that applies the stored theme
170
+ // before the application starts; so is `light`, which the `theme` option uses.
171
+ const apply = (theme) => {
172
+ if (theme && theme !== 'light') {
173
+ document.documentElement.setAttribute('data-theme', theme);
73
174
  }
74
175
  else {
75
176
  document.documentElement.removeAttribute('data-theme');
76
177
  }
178
+ reportBackground();
179
+ };
180
+ const pinned = () => ɵdemoThemeFromFragment(window.location.hash);
181
+ const site = () => {
182
+ try {
183
+ return window.localStorage.getItem(NG_DOC_STORE_THEME_KEY);
184
+ }
185
+ catch {
186
+ return null;
187
+ }
188
+ };
189
+ // The documentation page and the demo page share the origin, so a theme the reader picks
190
+ // reaches this page as a storage event; a theme of the fragment wins over it.
191
+ const onStorage = (event) => {
192
+ if (event.key !== NG_DOC_STORE_THEME_KEY || event.newValue === null || pinned())
193
+ return;
194
+ apply(event.newValue);
77
195
  };
196
+ // The documentation page changes the fragment with `location.replace`, without a reload.
197
+ const onHashChange = () => apply(pinned() ?? site());
198
+ // The `auto` theme follows the system's color scheme.
199
+ const scheme = window.matchMedia?.('(prefers-color-scheme: dark)');
200
+ if (pinned())
201
+ apply(pinned());
202
+ reportBackground();
78
203
  window.addEventListener('storage', onStorage);
79
- destroyRef.onDestroy(() => window.removeEventListener('storage', onStorage));
204
+ window.addEventListener('hashchange', onHashChange);
205
+ scheme?.addEventListener?.('change', reportBackground);
206
+ destroyRef.onDestroy(() => {
207
+ window.removeEventListener('storage', onStorage);
208
+ window.removeEventListener('hashchange', onHashChange);
209
+ scheme?.removeEventListener?.('change', reportBackground);
210
+ });
80
211
  }
81
212
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocDemoAppComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
82
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.2.1", type: NgDocDemoAppComponent, isStandalone: true, selector: "ng-doc-demo-app", ngImport: i0, template: '<router-outlet />', isInline: true, styles: [":host{display:flow-root}\n"], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
213
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.2.1", type: NgDocDemoAppComponent, isStandalone: true, selector: "ng-doc-demo-app", ngImport: i0, template: '<router-outlet />', isInline: true, styles: ["ng-doc-demo-app{display:flow-root}ng-doc-demo-host,ng-doc-playground-host{display:flex;justify-content:center;min-width:0}html.ng-doc-demo-embedded,html.ng-doc-demo-embedded body{background-color:transparent}\n"], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
83
214
  }
84
215
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocDemoAppComponent, decorators: [{
85
216
  type: Component,
86
- args: [{ selector: 'ng-doc-demo-app', imports: [RouterOutlet], template: '<router-outlet />', changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:flow-root}\n"] }]
217
+ args: [{ selector: 'ng-doc-demo-app', imports: [RouterOutlet], template: '<router-outlet />', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["ng-doc-demo-app{display:flow-root}ng-doc-demo-host,ng-doc-playground-host{display:flex;justify-content:center;min-width:0}html.ng-doc-demo-embedded,html.ng-doc-demo-embedded body{background-color:transparent}\n"] }]
87
218
  }], ctorParameters: () => [] });
88
219
 
89
220
  /**
@@ -168,9 +299,481 @@ async function ɵngDocDemoApplicationConfig(routes, demoProviders, options = {})
168
299
  };
169
300
  }
170
301
 
302
+ /** The key of a tagged value: a value JSON has no form for (`undefined`, `NaN`, a `Date`, …). */
303
+ const TAG = '$ngdoc';
304
+ /** Deeper values are refused: a playground input never needs them, and a cycle would recurse. */
305
+ const MAX_DEPTH = 32;
306
+ /**
307
+ * The longest encoded state an "Open in a new tab" URL carries; a longer one is left out.
308
+ * @internal
309
+ */
310
+ const ɵNG_DOC_PLAYGROUND_STATE_URL_LIMIT = 8192;
311
+ /**
312
+ * Encodes a value for the frame of an isolated playground. Only plain data is accepted:
313
+ * strings, numbers (`NaN`, the infinities and `-0` included), booleans, `null`, `undefined`, `Date`s,
314
+ * arrays and plain objects (prototype `Object.prototype` or `null`, own enumerable data
315
+ * properties). Anything else (a function, a symbol, a `bigint`, a class instance such as a
316
+ * `TemplateRef`, a `Map` or a signal, a getter, a cycle) is refused rather than sent changed: a
317
+ * structured clone would turn a class instance into a plain object without its behaviour, and
318
+ * the demo would then run with a value that isn't the one the inspector shows.
319
+ * @param value - The value of a playground input.
320
+ * @internal
321
+ */
322
+ function ɵencodePlaygroundValue(value) {
323
+ try {
324
+ return { ok: true, value: encode(value, '', 0, new Set()) };
325
+ }
326
+ catch (error) {
327
+ if (error instanceof Unencodable)
328
+ return { ok: false, path: error.path, reason: error.reason };
329
+ throw error;
330
+ }
331
+ }
332
+ /**
333
+ * Decodes a value that {@link ɵencodePlaygroundValue} encoded. Throws on anything it can't have
334
+ * produced, so a malformed message or URL never reaches the demo half-decoded.
335
+ * @param value - An encoded value.
336
+ * @internal
337
+ */
338
+ function ɵdecodePlaygroundValue(value) {
339
+ return decode(value, '', 0);
340
+ }
341
+ /**
342
+ * The encoded values of an "Open in a new tab" URL: base64url of their JSON, or `undefined`
343
+ * when it would be longer than {@link ɵNG_DOC_PLAYGROUND_STATE_URL_LIMIT}.
344
+ * @param values - Encoded values, by input name.
345
+ * @internal
346
+ */
347
+ function ɵplaygroundStateToUrl(values) {
348
+ const bytes = new TextEncoder().encode(JSON.stringify(values));
349
+ let binary = '';
350
+ for (const byte of bytes)
351
+ binary += String.fromCharCode(byte);
352
+ const text = btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
353
+ return text.length > ɵNG_DOC_PLAYGROUND_STATE_URL_LIMIT ? undefined : text;
354
+ }
355
+ /**
356
+ * The encoded values of an "Open in a new tab" URL, or `undefined` when the parameter isn't one
357
+ * that {@link ɵplaygroundStateToUrl} wrote.
358
+ * @param text - The `state` query parameter.
359
+ * @internal
360
+ */
361
+ function ɵplaygroundStateFromUrl(text) {
362
+ if (!/^[A-Za-z0-9_-]*$/.test(text) || text.length > ɵNG_DOC_PLAYGROUND_STATE_URL_LIMIT) {
363
+ return undefined;
364
+ }
365
+ try {
366
+ const binary = atob(text.replace(/-/g, '+').replace(/_/g, '/'));
367
+ const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
368
+ const parsed = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
369
+ if (!isPlainObject(parsed))
370
+ return undefined;
371
+ // Every value must decode; the decoded form is computed again where it is used.
372
+ for (const value of Object.values(parsed))
373
+ decode(value, '', 0);
374
+ return parsed;
375
+ }
376
+ catch {
377
+ return undefined;
378
+ }
379
+ }
380
+ class Unencodable extends Error {
381
+ constructor(path, reason) {
382
+ super(`${path || 'value'}: ${reason}`);
383
+ this.path = path;
384
+ this.reason = reason;
385
+ }
386
+ }
387
+ /**
388
+ *
389
+ * @param value
390
+ */
391
+ function isPlainObject(value) {
392
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
393
+ return false;
394
+ const prototype = Object.getPrototypeOf(value);
395
+ return prototype === Object.prototype || prototype === null;
396
+ }
397
+ /**
398
+ *
399
+ * @param path
400
+ * @param key
401
+ */
402
+ function child(path, key) {
403
+ if (typeof key === 'number')
404
+ return `${path}[${key}]`;
405
+ return /^[A-Za-z_$][\w$]*$/.test(key)
406
+ ? path
407
+ ? `${path}.${key}`
408
+ : key
409
+ : `${path}[${JSON.stringify(key)}]`;
410
+ }
411
+ /**
412
+ *
413
+ * @param tag
414
+ * @param value
415
+ */
416
+ function tagged(tag, value) {
417
+ return value === undefined ? { [TAG]: tag } : { [TAG]: tag, value };
418
+ }
419
+ /**
420
+ *
421
+ * @param value
422
+ * @param path
423
+ * @param depth
424
+ * @param ancestors
425
+ */
426
+ function encode(value, path, depth, ancestors) {
427
+ switch (typeof value) {
428
+ case 'string':
429
+ case 'boolean':
430
+ return value;
431
+ case 'number':
432
+ // JSON writes -0 as 0, so -0 is tagged like the numbers JSON has no form for.
433
+ return Number.isFinite(value) && !Object.is(value, -0)
434
+ ? value
435
+ : tagged('number', Object.is(value, -0) ? '-0' : String(value));
436
+ case 'undefined':
437
+ return tagged('undefined');
438
+ case 'function':
439
+ throw new Unencodable(path, 'a function');
440
+ case 'symbol':
441
+ throw new Unencodable(path, 'a symbol');
442
+ case 'bigint':
443
+ throw new Unencodable(path, 'a bigint');
444
+ }
445
+ if (value === null)
446
+ return null;
447
+ if (depth >= MAX_DEPTH)
448
+ throw new Unencodable(path, `nested deeper than ${MAX_DEPTH} levels`);
449
+ if (value instanceof Date) {
450
+ const time = value.getTime();
451
+ return tagged('date', Number.isNaN(time) ? 'Invalid Date' : value.toISOString());
452
+ }
453
+ const object = value;
454
+ if (ancestors.has(object))
455
+ throw new Unencodable(path, 'a cyclic reference');
456
+ ancestors.add(object);
457
+ try {
458
+ if (Array.isArray(object)) {
459
+ // A hole reads as `undefined`, as the code view prints it.
460
+ return Array.from(object, (item, index) => encode(item, child(path, index), depth + 1, ancestors));
461
+ }
462
+ if (!isPlainObject(object)) {
463
+ const name = object.constructor?.name;
464
+ throw new Unencodable(path, typeof name === 'string' && name ? `an instance of ${name}` : 'not a plain object');
465
+ }
466
+ if (Object.getOwnPropertySymbols(object).length) {
467
+ throw new Unencodable(path, 'an object with symbol keys');
468
+ }
469
+ const entries = [];
470
+ for (const key of Object.keys(object)) {
471
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
472
+ if (descriptor && !('value' in descriptor)) {
473
+ throw new Unencodable(child(path, key), 'a getter');
474
+ }
475
+ entries.push([key, encode(descriptor?.value, child(path, key), depth + 1, ancestors)]);
476
+ }
477
+ const encoded = Object.fromEntries(entries);
478
+ // An object of the user's that has the tag's key is wrapped, so it never reads as a tag.
479
+ return Object.prototype.hasOwnProperty.call(object, TAG) ? tagged('object', encoded) : encoded;
480
+ }
481
+ finally {
482
+ ancestors.delete(object);
483
+ }
484
+ }
485
+ /**
486
+ *
487
+ * @param value
488
+ * @param path
489
+ * @param depth
490
+ */
491
+ function decode(value, path, depth) {
492
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
493
+ return value;
494
+ if (typeof value === 'number') {
495
+ if (!Number.isFinite(value))
496
+ throw new Error(`${path || 'value'}: not a finite number`);
497
+ return value;
498
+ }
499
+ if (depth >= MAX_DEPTH)
500
+ throw new Error(`${path || 'value'}: nested too deep`);
501
+ if (Array.isArray(value)) {
502
+ return value.map((item, index) => decode(item, child(path, index), depth + 1));
503
+ }
504
+ if (!isPlainObject(value))
505
+ throw new Error(`${path || 'value'}: not an encoded value`);
506
+ if (Object.prototype.hasOwnProperty.call(value, TAG)) {
507
+ const keys = Object.keys(value).sort().join(',');
508
+ const tag = value[TAG];
509
+ const content = value['value'];
510
+ if (tag === 'undefined' && keys === TAG)
511
+ return undefined;
512
+ if (tag === 'number' && keys === `${TAG},value`) {
513
+ if (content === 'NaN')
514
+ return Number.NaN;
515
+ if (content === 'Infinity')
516
+ return Number.POSITIVE_INFINITY;
517
+ if (content === '-Infinity')
518
+ return Number.NEGATIVE_INFINITY;
519
+ if (content === '-0')
520
+ return -0;
521
+ }
522
+ if (tag === 'date' && keys === `${TAG},value` && typeof content === 'string') {
523
+ const date = new Date(content === 'Invalid Date' ? Number.NaN : content);
524
+ if (content === 'Invalid Date' || !Number.isNaN(date.getTime()))
525
+ return date;
526
+ }
527
+ if (tag === 'object' && keys === `${TAG},value` && isPlainObject(content)) {
528
+ return decodeObject(content, path, depth);
529
+ }
530
+ throw new Error(`${path || 'value'}: not an encoded value`);
531
+ }
532
+ return decodeObject(value, path, depth);
533
+ }
534
+ /**
535
+ *
536
+ * @param value
537
+ * @param path
538
+ * @param depth
539
+ */
540
+ function decodeObject(value, path, depth) {
541
+ return Object.fromEntries(Object.keys(value).map((key) => [key, decode(value[key], child(path, key), depth + 1)]));
542
+ }
543
+
544
+ /** Fragment parameters of the playground itself; an input name never starts with `~`. */
545
+ const INPUTS = '~inputs';
546
+ const CONTENT = '~content';
547
+ const RECREATE = '~recreate';
548
+ const VALUES = '~v';
549
+ const DATA = '~data';
550
+ /** The text JSON gives a number, which a readable string value must not look like. */
551
+ const NUMBER = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
552
+ /**
553
+ * Writes a state as a URL fragment (without `#`). A string, a finite number or a boolean is its
554
+ * own readable parameter (`label=Save&size=2&disabled=true`; a string that would read as another
555
+ * type is quoted as JSON). Every other value goes into one `~v` parameter, encoded by
556
+ * {@link ɵencodePlaygroundValue}. The frame reads the fragment with
557
+ * {@link ɵplaygroundStateFromFragment}, and the same fragment opens the page in a new tab.
558
+ * @param state - The state of the playground.
559
+ * @internal
560
+ */
561
+ function ɵplaygroundStateToFragment(state) {
562
+ const parameters = [`${INPUTS}=${state.inputs.map(encodeURIComponent).join(',')}`];
563
+ const encoded = {};
564
+ for (const [name, value] of Object.entries(state.properties)) {
565
+ const readable = readableText(value);
566
+ if (readable !== undefined) {
567
+ parameters.push(`${encodeURIComponent(name)}=${encodeURIComponent(readable)}`);
568
+ continue;
569
+ }
570
+ const result = ɵencodePlaygroundValue(value);
571
+ if (!result.ok) {
572
+ return {
573
+ ok: false,
574
+ path: result.path ? `${name}.${result.path}` : name,
575
+ reason: result.reason,
576
+ };
577
+ }
578
+ encoded[name] = result.value;
579
+ }
580
+ if (Object.keys(encoded).length) {
581
+ const text = ɵplaygroundStateToUrl(encoded);
582
+ if (text === undefined)
583
+ return tooLong();
584
+ parameters.push(`${VALUES}=${text}`);
585
+ }
586
+ if (state.content.length) {
587
+ parameters.push(`${CONTENT}=${state.content.map(encodeURIComponent).join(',')}`);
588
+ }
589
+ if (state.recreate)
590
+ parameters.push(`${RECREATE}=1`);
591
+ if (state.data && Object.keys(state.data).length) {
592
+ const data = {};
593
+ for (const [name, value] of Object.entries(state.data)) {
594
+ const result = ɵencodePlaygroundValue(value);
595
+ if (!result.ok) {
596
+ return {
597
+ ok: false,
598
+ path: ['data', name, result.path].filter(Boolean).join('.'),
599
+ reason: result.reason,
600
+ };
601
+ }
602
+ data[name] = result.value;
603
+ }
604
+ const text = ɵplaygroundStateToUrl(data);
605
+ if (text === undefined)
606
+ return tooLong();
607
+ parameters.push(`${DATA}=${text}`);
608
+ }
609
+ const fragment = parameters.join('&');
610
+ return fragment.length > ɵNG_DOC_PLAYGROUND_STATE_URL_LIMIT ? tooLong() : { ok: true, fragment };
611
+ }
612
+ /**
613
+ * Reads a state that {@link ɵplaygroundStateToFragment} wrote. Throws on a fragment it can't
614
+ * have written, so a malformed URL never reaches the target half-read.
615
+ * @param fragment - The fragment, with or without the leading `#`.
616
+ * @internal
617
+ */
618
+ function ɵplaygroundStateFromFragment(fragment) {
619
+ const text = fragment.replace(/^#/, '');
620
+ const properties = {};
621
+ let inputs;
622
+ let content = [];
623
+ let recreate = false;
624
+ let data;
625
+ if (text.length > ɵNG_DOC_PLAYGROUND_STATE_URL_LIMIT)
626
+ throw new Error('The state is too long.');
627
+ for (const parameter of text ? text.split('&') : []) {
628
+ const separator = parameter.indexOf('=');
629
+ if (separator < 1)
630
+ throw new Error(`Malformed parameter: ${parameter}`);
631
+ const name = decodeURIComponent(parameter.slice(0, separator));
632
+ const value = parameter.slice(separator + 1);
633
+ if (name === INPUTS)
634
+ inputs = list(value);
635
+ else if (name === CONTENT)
636
+ content = list(value);
637
+ else if (name === RECREATE)
638
+ recreate = value === '1';
639
+ else if (name === VALUES) {
640
+ const values = ɵplaygroundStateFromUrl(value);
641
+ if (!values)
642
+ throw new Error('Malformed encoded values.');
643
+ for (const [key, item] of Object.entries(values))
644
+ properties[key] = ɵdecodePlaygroundValue(item);
645
+ }
646
+ else if (name === DATA) {
647
+ const values = ɵplaygroundStateFromUrl(value);
648
+ if (!values)
649
+ throw new Error('Malformed data.');
650
+ data = {};
651
+ for (const [key, item] of Object.entries(values))
652
+ data[key] = ɵdecodePlaygroundValue(item);
653
+ }
654
+ else if (ɵisDemoThemeParameter(name)) {
655
+ // The demo application applies the theme; it is not part of the playground's state.
656
+ }
657
+ else if (name.startsWith('~')) {
658
+ throw new Error(`Unknown parameter: ${name}`);
659
+ }
660
+ else {
661
+ properties[name] = readValue(decodeURIComponent(value));
662
+ }
663
+ }
664
+ if (!inputs)
665
+ throw new Error('The state names no inputs.');
666
+ return { inputs, properties, content, recreate, ...(data ? { data } : {}) };
667
+ }
668
+ /**
669
+ *
670
+ */
671
+ function tooLong() {
672
+ return { ok: false, path: '', reason: 'the values are too long for the URL of the frame' };
673
+ }
674
+ /**
675
+ *
676
+ * @param value
677
+ */
678
+ function list(value) {
679
+ return value ? value.split(',').map(decodeURIComponent) : [];
680
+ }
681
+ /**
682
+ * The readable text of a value, or `undefined` when it goes into the encoded parameter.
683
+ * @param value
684
+ */
685
+ function readableText(value) {
686
+ if (typeof value === 'boolean')
687
+ return String(value);
688
+ if (typeof value === 'number') {
689
+ return Number.isFinite(value) && !Object.is(value, -0) ? JSON.stringify(value) : undefined;
690
+ }
691
+ if (typeof value !== 'string')
692
+ return undefined;
693
+ return value === 'true' || value === 'false' || NUMBER.test(value) || value.startsWith('"')
694
+ ? JSON.stringify(value)
695
+ : value;
696
+ }
697
+ /**
698
+ *
699
+ * @param text
700
+ */
701
+ function readValue(text) {
702
+ if (text === 'true')
703
+ return true;
704
+ if (text === 'false')
705
+ return false;
706
+ if (NUMBER.test(text))
707
+ return Number(text);
708
+ if (text.startsWith('"')) {
709
+ const parsed = JSON.parse(text);
710
+ if (typeof parsed !== 'string')
711
+ throw new Error(`Malformed string: ${text}`);
712
+ return parsed;
713
+ }
714
+ return text;
715
+ }
716
+
717
+ /**
718
+ *
719
+ * @param value
720
+ * @param type
721
+ */
722
+ function header(value, type) {
723
+ const message = value;
724
+ return (typeof message === 'object' &&
725
+ message !== null &&
726
+ message['source'] === ɵNG_DOC_DEMO_MESSAGE_SOURCE &&
727
+ message['version'] === 1 &&
728
+ message['type'] === type);
729
+ }
730
+ /**
731
+ *
732
+ * @param value
733
+ */
734
+ function isRecord(value) {
735
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
736
+ }
737
+ /**
738
+ * Whether every value of a record decodes: a malformed value is a malformed message.
739
+ * @param value
740
+ */
741
+ function isEncodedRecord(value) {
742
+ if (!isRecord(value))
743
+ return false;
744
+ try {
745
+ for (const item of Object.values(value))
746
+ ɵdecodePlaygroundValue(item);
747
+ return true;
748
+ }
749
+ catch {
750
+ return false;
751
+ }
752
+ }
753
+ /**
754
+ * Whether a message comes from the frame of an isolated playground, and is one it sends.
755
+ * @param value - The data of a `message` event.
756
+ * @internal
757
+ */
758
+ function ɵisNgDocPlaygroundFrameMessage(value) {
759
+ if (header(value, 'playground-ready')) {
760
+ const uncloneable = value['uncloneable'];
761
+ return (isEncodedRecord(value['defaults']) &&
762
+ Array.isArray(uncloneable) &&
763
+ uncloneable.every((name) => typeof name === 'string'));
764
+ }
765
+ if (header(value, 'playground-applied'))
766
+ return typeof value['fragment'] === 'string';
767
+ if (header(value, 'playground-error')) {
768
+ return ((value['phase'] === 'create' || value['phase'] === 'apply') &&
769
+ typeof value['message'] === 'string');
770
+ }
771
+ return false;
772
+ }
773
+
171
774
  /**
172
775
  * Generated bundle index. Do not edit.
173
776
  */
174
777
 
175
- export { NgDocDemoAppComponent, NgDocDemoHostComponent, ɵNG_DOC_DEMO_MESSAGE_SOURCE, ɵisNgDocDemoSizeMessage, ɵngDocDemoApplicationConfig };
778
+ export { NgDocDemoAppComponent, NgDocDemoHostComponent, ɵNG_DOC_DEMO_MESSAGE_SOURCE, ɵNG_DOC_PLAYGROUND_STATE_URL_LIMIT, ɵdecodePlaygroundValue, ɵdemoThemeFromFragment, ɵencodePlaygroundValue, ɵisDemoThemeParameter, ɵisNgDocDemoBackgroundMessage, ɵisNgDocDemoSizeMessage, ɵisNgDocPlaygroundFrameMessage, ɵngDocDemoApplicationConfig, ɵplaygroundStateFromFragment, ɵplaygroundStateFromUrl, ɵplaygroundStateToFragment, ɵplaygroundStateToUrl, ɵwithDemoTheme };
176
779
  //# sourceMappingURL=ng-doc-app-demo-app.mjs.map