@ng-doc/app 22.0.0-beta.2 → 22.0.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 (203) hide show
  1. package/README.md +20 -29
  2. package/fesm2022/ng-doc-app-classes-content-anchor-controller.mjs +363 -0
  3. package/fesm2022/ng-doc-app-classes-content-anchor-controller.mjs.map +1 -0
  4. package/fesm2022/ng-doc-app-classes-content-controller.mjs +299 -0
  5. package/fesm2022/ng-doc-app-classes-content-controller.mjs.map +1 -0
  6. package/fesm2022/ng-doc-app-classes-default-search-engine.mjs +51 -35
  7. package/fesm2022/ng-doc-app-classes-default-search-engine.mjs.map +1 -1
  8. package/fesm2022/ng-doc-app-classes-root-page.mjs.map +1 -1
  9. package/fesm2022/ng-doc-app-classes-search-engine.mjs.map +1 -1
  10. package/fesm2022/ng-doc-app-classes.mjs +2 -0
  11. package/fesm2022/ng-doc-app-classes.mjs.map +1 -1
  12. package/fesm2022/ng-doc-app-components-api-list.mjs +173 -69
  13. package/fesm2022/ng-doc-app-components-api-list.mjs.map +1 -1
  14. package/fesm2022/ng-doc-app-components-breadcrumb.mjs +13 -10
  15. package/fesm2022/ng-doc-app-components-breadcrumb.mjs.map +1 -1
  16. package/fesm2022/ng-doc-app-components-code.mjs +152 -25
  17. package/fesm2022/ng-doc-app-components-code.mjs.map +1 -1
  18. package/fesm2022/ng-doc-app-components-command-palette.mjs +498 -0
  19. package/fesm2022/ng-doc-app-components-command-palette.mjs.map +1 -0
  20. package/fesm2022/ng-doc-app-components-copy-button.mjs +38 -21
  21. package/fesm2022/ng-doc-app-components-copy-button.mjs.map +1 -1
  22. package/fesm2022/ng-doc-app-components-demo-displayer.mjs +36 -29
  23. package/fesm2022/ng-doc-app-components-demo-displayer.mjs.map +1 -1
  24. package/fesm2022/ng-doc-app-components-demo-pane.mjs +39 -36
  25. package/fesm2022/ng-doc-app-components-demo-pane.mjs.map +1 -1
  26. package/fesm2022/ng-doc-app-components-demo.mjs +129 -44
  27. package/fesm2022/ng-doc-app-components-demo.mjs.map +1 -1
  28. package/fesm2022/ng-doc-app-components-fullscreen-button.mjs +12 -9
  29. package/fesm2022/ng-doc-app-components-fullscreen-button.mjs.map +1 -1
  30. package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs +79 -0
  31. package/fesm2022/ng-doc-app-components-fullscreen-toggle.mjs.map +1 -0
  32. package/fesm2022/ng-doc-app-components-heading-anchor.mjs +54 -21
  33. package/fesm2022/ng-doc-app-components-heading-anchor.mjs.map +1 -1
  34. package/fesm2022/ng-doc-app-components-image-viewer.mjs +183 -89
  35. package/fesm2022/ng-doc-app-components-image-viewer.mjs.map +1 -1
  36. package/fesm2022/ng-doc-app-components-kind-icon.mjs +42 -25
  37. package/fesm2022/ng-doc-app-components-kind-icon.mjs.map +1 -1
  38. package/fesm2022/ng-doc-app-components-link.mjs +12 -9
  39. package/fesm2022/ng-doc-app-components-link.mjs.map +1 -1
  40. package/fesm2022/ng-doc-app-components-members.mjs +30 -0
  41. package/fesm2022/ng-doc-app-components-members.mjs.map +1 -0
  42. package/fesm2022/ng-doc-app-components-mermaid-viewer.mjs +61 -20
  43. package/fesm2022/ng-doc-app-components-mermaid-viewer.mjs.map +1 -1
  44. package/fesm2022/ng-doc-app-components-navbar.mjs +39 -49
  45. package/fesm2022/ng-doc-app-components-navbar.mjs.map +1 -1
  46. package/fesm2022/ng-doc-app-components-page-header.mjs +73 -10
  47. package/fesm2022/ng-doc-app-components-page-header.mjs.map +1 -1
  48. package/fesm2022/ng-doc-app-components-page-link.mjs +123 -31
  49. package/fesm2022/ng-doc-app-components-page-link.mjs.map +1 -1
  50. package/fesm2022/ng-doc-app-components-page-navigation.mjs +17 -19
  51. package/fesm2022/ng-doc-app-components-page-navigation.mjs.map +1 -1
  52. package/fesm2022/ng-doc-app-components-page-wrapper.mjs +56 -35
  53. package/fesm2022/ng-doc-app-components-page-wrapper.mjs.map +1 -1
  54. package/fesm2022/ng-doc-app-components-page.mjs +168 -24
  55. package/fesm2022/ng-doc-app-components-page.mjs.map +1 -1
  56. package/fesm2022/ng-doc-app-components-playground.mjs +392 -263
  57. package/fesm2022/ng-doc-app-components-playground.mjs.map +1 -1
  58. package/fesm2022/ng-doc-app-components-root.mjs +48 -31
  59. package/fesm2022/ng-doc-app-components-root.mjs.map +1 -1
  60. package/fesm2022/ng-doc-app-components-search-result.mjs +22 -21
  61. package/fesm2022/ng-doc-app-components-search-result.mjs.map +1 -1
  62. package/fesm2022/ng-doc-app-components-search.mjs +64 -33
  63. package/fesm2022/ng-doc-app-components-search.mjs.map +1 -1
  64. package/fesm2022/ng-doc-app-components-sidebar.mjs +86 -61
  65. package/fesm2022/ng-doc-app-components-sidebar.mjs.map +1 -1
  66. package/fesm2022/ng-doc-app-components-tabs.mjs +18 -12
  67. package/fesm2022/ng-doc-app-components-tabs.mjs.map +1 -1
  68. package/fesm2022/ng-doc-app-components-theme-toggle.mjs +33 -21
  69. package/fesm2022/ng-doc-app-components-theme-toggle.mjs.map +1 -1
  70. package/fesm2022/ng-doc-app-components-toc.mjs +267 -90
  71. package/fesm2022/ng-doc-app-components-toc.mjs.map +1 -1
  72. package/fesm2022/ng-doc-app-components.mjs +3 -1
  73. package/fesm2022/ng-doc-app-components.mjs.map +1 -1
  74. package/fesm2022/ng-doc-app-defaults-default-processors.mjs +2 -1
  75. package/fesm2022/ng-doc-app-defaults-default-processors.mjs.map +1 -1
  76. package/fesm2022/ng-doc-app-directives-code-highlighter.mjs +11 -5
  77. package/fesm2022/ng-doc-app-directives-code-highlighter.mjs.map +1 -1
  78. package/fesm2022/ng-doc-app-directives-members-filter.mjs +276 -0
  79. package/fesm2022/ng-doc-app-directives-members-filter.mjs.map +1 -0
  80. package/fesm2022/ng-doc-app-directives-route-active.mjs +34 -30
  81. package/fesm2022/ng-doc-app-directives-route-active.mjs.map +1 -1
  82. package/fesm2022/ng-doc-app-directives.mjs +1 -0
  83. package/fesm2022/ng-doc-app-directives.mjs.map +1 -1
  84. package/fesm2022/ng-doc-app-helpers.mjs +285 -1
  85. package/fesm2022/ng-doc-app-helpers.mjs.map +1 -1
  86. package/fesm2022/ng-doc-app-pipes-decode-uri-component.mjs +6 -4
  87. package/fesm2022/ng-doc-app-pipes-decode-uri-component.mjs.map +1 -1
  88. package/fesm2022/ng-doc-app-pipes-extract-value.mjs +6 -4
  89. package/fesm2022/ng-doc-app-pipes-extract-value.mjs.map +1 -1
  90. package/fesm2022/ng-doc-app-pipes-filter-by-text.mjs +7 -4
  91. package/fesm2022/ng-doc-app-pipes-filter-by-text.mjs.map +1 -1
  92. package/fesm2022/ng-doc-app-pipes-sanitize-html.mjs +8 -5
  93. package/fesm2022/ng-doc-app-pipes-sanitize-html.mjs.map +1 -1
  94. package/fesm2022/ng-doc-app-processors-page-processor.mjs +86 -23
  95. package/fesm2022/ng-doc-app-processors-page-processor.mjs.map +1 -1
  96. package/fesm2022/ng-doc-app-processors-processors-tooltip.mjs +26 -25
  97. package/fesm2022/ng-doc-app-processors-processors-tooltip.mjs.map +1 -1
  98. package/fesm2022/ng-doc-app-processors-processors.mjs +12 -1
  99. package/fesm2022/ng-doc-app-processors-processors.mjs.map +1 -1
  100. package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs +31 -2
  101. package/fesm2022/ng-doc-app-providers-ng-doc-app.mjs.map +1 -1
  102. package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs +122 -0
  103. package/fesm2022/ng-doc-app-services-content-scroll-intent.mjs.map +1 -0
  104. package/fesm2022/ng-doc-app-services-content-state.mjs +57 -0
  105. package/fesm2022/ng-doc-app-services-content-state.mjs.map +1 -0
  106. package/fesm2022/ng-doc-app-services-fullscreen-route.mjs +74 -0
  107. package/fesm2022/ng-doc-app-services-fullscreen-route.mjs.map +1 -0
  108. package/fesm2022/ng-doc-app-services-highlighter.mjs +83 -14
  109. package/fesm2022/ng-doc-app-services-highlighter.mjs.map +1 -1
  110. package/fesm2022/ng-doc-app-services-route-preloader.mjs +360 -0
  111. package/fesm2022/ng-doc-app-services-route-preloader.mjs.map +1 -0
  112. package/fesm2022/ng-doc-app-services-shortcuts.mjs +214 -0
  113. package/fesm2022/ng-doc-app-services-shortcuts.mjs.map +1 -0
  114. package/fesm2022/ng-doc-app-services-sidebar.mjs +59 -25
  115. package/fesm2022/ng-doc-app-services-sidebar.mjs.map +1 -1
  116. package/fesm2022/ng-doc-app-services-store.mjs +8 -8
  117. package/fesm2022/ng-doc-app-services-store.mjs.map +1 -1
  118. package/fesm2022/ng-doc-app-services-theme.mjs +32 -12
  119. package/fesm2022/ng-doc-app-services-theme.mjs.map +1 -1
  120. package/fesm2022/ng-doc-app-services.mjs +143 -0
  121. package/fesm2022/ng-doc-app-services.mjs.map +1 -1
  122. package/fesm2022/ng-doc-app-tokens.mjs +7 -28
  123. package/fesm2022/ng-doc-app-tokens.mjs.map +1 -1
  124. package/fesm2022/ng-doc-app-type-controls-boolean-control.mjs +18 -9
  125. package/fesm2022/ng-doc-app-type-controls-boolean-control.mjs.map +1 -1
  126. package/fesm2022/ng-doc-app-type-controls-number-control.mjs +12 -9
  127. package/fesm2022/ng-doc-app-type-controls-number-control.mjs.map +1 -1
  128. package/fesm2022/ng-doc-app-type-controls-string-control.mjs +12 -9
  129. package/fesm2022/ng-doc-app-type-controls-string-control.mjs.map +1 -1
  130. package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs +22 -16
  131. package/fesm2022/ng-doc-app-type-controls-type-alias-control.mjs.map +1 -1
  132. package/package.json +69 -28
  133. package/styles/global.css +1125 -201
  134. package/styles/main.css +1125 -201
  135. package/styles/themes/dark.css +78 -56
  136. package/types/ng-doc-app-classes-content-anchor-controller.d.ts +54 -0
  137. package/types/ng-doc-app-classes-content-controller.d.ts +94 -0
  138. package/types/ng-doc-app-classes-default-search-engine.d.ts +9 -1
  139. package/types/ng-doc-app-classes-root-page.d.ts +6 -1
  140. package/types/ng-doc-app-classes-search-engine.d.ts +5 -0
  141. package/types/ng-doc-app-classes.d.ts +2 -0
  142. package/types/ng-doc-app-components-api-list.d.ts +69 -20
  143. package/types/ng-doc-app-components-breadcrumb.d.ts +8 -3
  144. package/types/ng-doc-app-components-code.d.ts +46 -12
  145. package/types/ng-doc-app-components-command-palette.d.ts +147 -0
  146. package/types/ng-doc-app-components-copy-button.d.ts +18 -6
  147. package/types/ng-doc-app-components-demo-displayer.d.ts +23 -10
  148. package/types/ng-doc-app-components-demo-pane.d.ts +19 -15
  149. package/types/ng-doc-app-components-demo.d.ts +62 -15
  150. package/types/ng-doc-app-components-fullscreen-button.d.ts +4 -2
  151. package/types/ng-doc-app-components-fullscreen-toggle.d.ts +24 -0
  152. package/types/ng-doc-app-components-heading-anchor.d.ts +26 -7
  153. package/types/ng-doc-app-components-image-viewer.d.ts +71 -7
  154. package/types/ng-doc-app-components-kind-icon.d.ts +28 -7
  155. package/types/ng-doc-app-components-link.d.ts +6 -2
  156. package/types/ng-doc-app-components-members.d.ts +13 -0
  157. package/types/ng-doc-app-components-mermaid-viewer.d.ts +21 -6
  158. package/types/ng-doc-app-components-navbar.d.ts +25 -15
  159. package/types/ng-doc-app-components-page-header.d.ts +28 -4
  160. package/types/ng-doc-app-components-page-link.d.ts +34 -15
  161. package/types/ng-doc-app-components-page-navigation.d.ts +10 -5
  162. package/types/ng-doc-app-components-page-wrapper.d.ts +35 -11
  163. package/types/ng-doc-app-components-page.d.ts +57 -6
  164. package/types/ng-doc-app-components-playground.d.ts +146 -78
  165. package/types/ng-doc-app-components-root.d.ts +28 -7
  166. package/types/ng-doc-app-components-search-result.d.ts +14 -6
  167. package/types/ng-doc-app-components-search.d.ts +19 -7
  168. package/types/ng-doc-app-components-tabs.d.ts +13 -4
  169. package/types/ng-doc-app-components-theme-toggle.d.ts +18 -5
  170. package/types/ng-doc-app-components-toc.d.ts +76 -29
  171. package/types/ng-doc-app-components.d.ts +3 -1
  172. package/types/ng-doc-app-directives-code-highlighter.d.ts +8 -4
  173. package/types/ng-doc-app-directives-members-filter.d.ts +61 -0
  174. package/types/ng-doc-app-directives-route-active.d.ts +21 -7
  175. package/types/ng-doc-app-directives.d.ts +1 -0
  176. package/types/ng-doc-app-helpers.d.ts +124 -3
  177. package/types/ng-doc-app-interfaces.d.ts +48 -28
  178. package/types/ng-doc-app-pipes-decode-uri-component.d.ts +3 -0
  179. package/types/ng-doc-app-pipes-extract-value.d.ts +3 -0
  180. package/types/ng-doc-app-pipes-filter-by-text.d.ts +4 -0
  181. package/types/ng-doc-app-pipes-sanitize-html.d.ts +4 -2
  182. package/types/ng-doc-app-processors-page-processor.d.ts +39 -10
  183. package/types/ng-doc-app-processors-processors-tooltip.d.ts +11 -10
  184. package/types/ng-doc-app-processors-processors.d.ts +5 -1
  185. package/types/ng-doc-app-providers-ng-doc-app.d.ts +17 -0
  186. package/types/ng-doc-app-services-content-scroll-intent.d.ts +52 -0
  187. package/types/ng-doc-app-services-content-state.d.ts +40 -0
  188. package/types/ng-doc-app-services-fullscreen-route.d.ts +35 -0
  189. package/types/ng-doc-app-services-highlighter.d.ts +40 -5
  190. package/types/ng-doc-app-services-route-preloader.d.ts +109 -0
  191. package/types/ng-doc-app-services-shortcuts.d.ts +96 -0
  192. package/types/ng-doc-app-services-sidebar.d.ts +24 -4
  193. package/types/ng-doc-app-services-store.d.ts +24 -1
  194. package/types/ng-doc-app-services-theme.d.ts +19 -3
  195. package/types/ng-doc-app-services.d.ts +77 -0
  196. package/types/ng-doc-app-tokens.d.ts +10 -30
  197. package/types/ng-doc-app-type-controls-boolean-control.d.ts +11 -6
  198. package/types/ng-doc-app-type-controls-number-control.d.ts +5 -3
  199. package/types/ng-doc-app-type-controls-string-control.d.ts +5 -3
  200. package/types/ng-doc-app-type-controls-type-alias-control.d.ts +13 -6
  201. package/fesm2022/ng-doc-app-components-search-dialog.mjs +0 -78
  202. package/fesm2022/ng-doc-app-components-search-dialog.mjs.map +0 -1
  203. package/types/ng-doc-app-components-search-dialog.d.ts +0 -28
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, Injector, ChangeDetectorRef, DestroyRef, ViewContainerRef, ViewChild, Input, ChangeDetectionStrategy, Component, HostBinding, EventEmitter, isDevMode, Output, Directive } from '@angular/core';
2
+ import { input, viewChild, ViewContainerRef, signal, inject, Injector, DestroyRef, PendingTasks, ChangeDetectionStrategy, Component, computed, effect, untracked, reflectComponentType, model, output, isDevMode, ChangeDetectorRef, Directive } from '@angular/core';
3
3
  import { extractFunctionDefaults } from '@ng-doc/core/helpers/extract-function-defaults';
4
4
  import { Subject, take } from 'rxjs';
5
5
  import * as i1 from '@angular/forms';
@@ -7,57 +7,84 @@ import { FormsModule, FormBuilder } from '@angular/forms';
7
7
  import { NgDocRootPage } from '@ng-doc/app/classes/root-page';
8
8
  import { isSameObject } from '@ng-doc/core/helpers/is-same-object';
9
9
  import { objectKeys } from '@ng-doc/core/helpers/object-keys';
10
- import { NgDocLabelComponent, NgDocTooltipDirective, NgDocTextComponent, NgDocButtonComponent, NgDocCheckboxComponent, NgDocIconComponent, NgDocTextRightDirective, NgDocBindPipe, NgDocExecutePipe, NgDocAsArrayPipe } from '@ng-doc/ui-kit';
10
+ import { NgDocLabelComponent, NgDocTooltipDirective, NgDocCheckboxComponent, NgDocIconComponent, NgDocBindPipe, NgDocExecutePipe, NgDocAsArrayPipe } from '@ng-doc/ui-kit';
11
11
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
12
12
  import { NgDocDemoDisplayerComponent } from '@ng-doc/app/components/demo-displayer';
13
13
  import { formatHtml, isPlaygroundProperty } from '@ng-doc/app/helpers';
14
14
  import { getPlaygroundDemoToken } from '@ng-doc/app/providers/playground-demo';
15
15
  import { buildPlaygroundDemoPipeTemplate, buildPlaygroundDemoTemplate } from '@ng-doc/core/helpers/build-playground-demo-template';
16
16
  import { stringify } from '@ng-doc/core/helpers/stringify';
17
- import { takeUntil, startWith, map } from 'rxjs/operators';
18
- import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
19
- import { AsyncPipe, KeyValuePipe } from '@angular/common';
17
+ import { startWith, takeUntil } from 'rxjs/operators';
18
+ import { KeyValuePipe } from '@angular/common';
20
19
  import { getTokenForType } from '@ng-doc/app/providers/type-control';
21
20
  import { extractValueOrThrow } from '@ng-doc/core/helpers/extract-value';
22
21
  import { isPresent } from '@ng-doc/core/helpers/is-present';
22
+ import { NgDocKindIconComponent } from '@ng-doc/app/components/kind-icon';
23
23
  import { NgDocSanitizeHtmlPipe } from '@ng-doc/app/pipes';
24
24
 
25
+ /**
26
+ * One demo of a playground: it creates the generated playground class of its selector (or pipe),
27
+ * passes it the form's values and shows the code of the current state.
28
+ */
25
29
  class NgDocPlaygroundDemoComponent {
26
30
  constructor() {
27
- this.id = '';
28
- this.pipeName = '';
29
- this.selector = '';
30
- this.recreateDemo = false;
31
- this.expanded = false;
32
- this.code = '';
31
+ /** The name of the playground in the page configuration. */
32
+ this.id = input('', /* @ts-ignore */
33
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
34
+ /** The name of the pipe, when the target is a pipe. */
35
+ this.pipeName = input('', /* @ts-ignore */
36
+ ...(ngDevMode ? [{ debugName: "pipeName" }] : /* istanbul ignore next */ []));
37
+ /** The selector of the target this demo renders. */
38
+ this.selector = input('', /* @ts-ignore */
39
+ ...(ngDevMode ? [{ debugName: "selector" }] : /* istanbul ignore next */ []));
40
+ /** The playground configuration, extended by the action's options. */
41
+ this.configuration = input(undefined, /* @ts-ignore */
42
+ ...(ngDevMode ? [{ debugName: "configuration" }] : /* istanbul ignore next */ []));
43
+ /** The inputs of the target. */
44
+ this.properties = input(undefined, /* @ts-ignore */
45
+ ...(ngDevMode ? [{ debugName: "properties" }] : /* istanbul ignore next */ []));
46
+ /** Whether the demo is created again each time a value changes. */
47
+ this.recreateDemo = input(false, /* @ts-ignore */
48
+ ...(ngDevMode ? [{ debugName: "recreateDemo" }] : /* istanbul ignore next */ []));
49
+ /** The values of the inputs and content slots. */
50
+ this.form = input(undefined, /* @ts-ignore */
51
+ ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
52
+ /** Whether the code of the demo is shown. */
53
+ this.expanded = input(false, /* @ts-ignore */
54
+ ...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
55
+ /** Where the playground class is created. */
56
+ this.demoOutlet = viewChild('demoOutlet', { ...(ngDevMode ? { debugName: "demoOutlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
57
+ this.code = signal('', /* @ts-ignore */
58
+ ...(ngDevMode ? [{ debugName: "code" }] : /* istanbul ignore next */ []));
33
59
  this.injector = inject(Injector);
34
- this.changeDetectorRef = inject(ChangeDetectorRef);
35
60
  this.destroyRef = inject(DestroyRef);
61
+ this.pendingTasks = inject(PendingTasks);
36
62
  this.unsubscribe$ = new Subject();
37
63
  }
38
64
  async ngOnChanges({ form, id }) {
39
65
  if (form || id) {
40
66
  this.unsubscribe$.next();
41
- const demoInjector = getPlaygroundDemoToken(this.id);
67
+ const demoInjector = getPlaygroundDemoToken(this.id());
42
68
  if (demoInjector) {
43
69
  const demos = this.injector.get(demoInjector, []);
44
- this.playgroundDemo = demos.find((demo) => demo.selector === this.selector || demo.selector === this.pipeName);
70
+ this.playgroundDemo = demos.find((demo) => demo.selector === this.selector() || demo.selector === this.pipeName());
45
71
  }
46
72
  await this.updateDemo();
47
- this.form?.valueChanges
48
- .pipe(takeUntil(this.unsubscribe$), startWith(this.form?.value), takeUntilDestroyed(this.destroyRef))
73
+ const form = this.form();
74
+ form?.valueChanges
75
+ .pipe(startWith(form.value), takeUntil(this.unsubscribe$), takeUntilDestroyed(this.destroyRef))
49
76
  .subscribe((data) => this.updateDemo(data));
50
77
  }
51
78
  }
52
79
  async updateDemo(data) {
53
- if (this.recreateDemo || !this.demoRef) {
80
+ if (this.recreateDemo() || !this.demoRef) {
54
81
  this.createDemo();
55
82
  }
56
83
  if (data) {
57
84
  this.demoRef?.setInput('properties', data.properties ?? {});
58
85
  this.demoRef?.setInput('content', data.content ?? {});
59
- this.demoRef?.setInput('actionData', this.configuration?.data ?? {});
60
- if (this.recreateDemo) {
86
+ this.demoRef?.setInput('actionData', this.configuration()?.data ?? {});
87
+ if (this.recreateDemo()) {
61
88
  this.demoRef?.instance.onReattached.subscribe(() => {
62
89
  this.demoRef?.changeDetectorRef.detectChanges();
63
90
  });
@@ -68,28 +95,35 @@ class NgDocPlaygroundDemoComponent {
68
95
  createDemo() {
69
96
  if (this.playgroundDemo) {
70
97
  this.demoRef?.destroy();
71
- this.demoRef = this.demoOutlet?.createComponent(this.playgroundDemo);
98
+ this.demoRef = this.demoOutlet()?.createComponent(this.playgroundDemo);
72
99
  this.demoRef?.changeDetectorRef.markForCheck();
73
100
  }
74
101
  }
75
102
  async updateCodeView() {
76
- const template = this.pipeName
77
- ? buildPlaygroundDemoPipeTemplate(this.configuration?.template ?? '', this.pipeName, this.getActiveContent(), this.getPipeActiveInputs())
78
- : buildPlaygroundDemoTemplate(this.configuration?.template ?? '', this.selector, this.getActiveContent(), this.getActiveInputs());
79
- this.code = await formatHtml(template);
80
- this.changeDetectorRef.markForCheck();
103
+ const template = this.pipeName()
104
+ ? buildPlaygroundDemoPipeTemplate(this.configuration()?.template ?? '', this.pipeName(), this.getActiveContent(), this.getPipeActiveInputs())
105
+ : buildPlaygroundDemoTemplate(this.configuration()?.template ?? '', this.selector(), this.getActiveContent(), this.getActiveInputs());
106
+ // Formatting loads the formatter asynchronously. Zoneless SSR waits only for pending tasks, so
107
+ // the page would otherwise be serialized before the code view is filled.
108
+ const done = this.pendingTasks.add();
109
+ try {
110
+ this.code.set(await formatHtml(template));
111
+ }
112
+ finally {
113
+ done();
114
+ }
81
115
  }
82
116
  getActiveContent() {
83
- const formData = this.form?.controls.content.value ?? {};
117
+ const formData = this.form()?.controls.content.value ?? {};
84
118
  return objectKeys(formData).reduce((result, key) => {
85
- result[key] = formData[key] ? this.configuration?.content?.[key].template ?? '' : '';
119
+ result[key] = formData[key] ? this.configuration()?.content?.[key].template ?? '' : '';
86
120
  return result;
87
121
  }, {});
88
122
  }
89
123
  getActiveInputs() {
90
- const formData = this.form?.controls.properties.value ?? {};
124
+ const formData = this.form()?.controls.properties.value ?? {};
91
125
  return objectKeys(formData).reduce((result, key) => {
92
- const inputName = this.properties?.[key]?.inputName ?? key;
126
+ const inputName = this.properties()?.[key]?.inputName ?? key;
93
127
  const value = formData[key];
94
128
  const property = this.demoRef?.instance?.defaultValues[key];
95
129
  if (property !== value) {
@@ -99,7 +133,7 @@ class NgDocPlaygroundDemoComponent {
99
133
  }, {});
100
134
  }
101
135
  getPipeActiveInputs() {
102
- const formData = this.form?.controls.properties.value ?? {};
136
+ const formData = this.form()?.controls.properties.value ?? {};
103
137
  let changedInputIndex = -1;
104
138
  return objectKeys(formData)
105
139
  .map((key, i) => {
@@ -120,128 +154,208 @@ class NgDocPlaygroundDemoComponent {
120
154
  this.unsubscribe$.next();
121
155
  this.unsubscribe$.complete();
122
156
  }
123
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocPlaygroundDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
124
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.6", type: NgDocPlaygroundDemoComponent, isStandalone: true, selector: "ng-doc-playground-demo", inputs: { id: "id", pipeName: "pipeName", selector: "selector", configuration: "configuration", properties: "properties", recreateDemo: "recreateDemo", form: "form", expanded: "expanded" }, viewQueries: [{ propertyName: "demoOutlet", first: true, predicate: ["demoOutlet"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: "<ng-doc-demo-displayer [code]=\"code\" [border]=\"false\" [expanded]=\"expanded\" language=\"angular-html\">\n <ng-container #demoOutlet></ng-container>\n</ng-doc-demo-displayer>\n", styles: [":host{display:block;border:var(--ng-doc-demo-displayer-border);border-radius:var(--ng-doc-demo-displayer-border-radius);overflow:hidden}:host:not(:last-child){border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0}:host:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}\n"], dependencies: [{ kind: "component", type: NgDocDemoDisplayerComponent, selector: "ng-doc-demo-displayer", inputs: ["codeContent", "code", "language", "container", "border", "expanded"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
157
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocPlaygroundDemoComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
158
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.2.1", type: NgDocPlaygroundDemoComponent, isStandalone: true, selector: "ng-doc-playground-demo", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, pipeName: { classPropertyName: "pipeName", publicName: "pipeName", isSignal: true, isRequired: false, transformFunction: null }, selector: { classPropertyName: "selector", publicName: "selector", isSignal: true, isRequired: false, transformFunction: null }, configuration: { classPropertyName: "configuration", publicName: "configuration", isSignal: true, isRequired: false, transformFunction: null }, properties: { classPropertyName: "properties", publicName: "properties", isSignal: true, isRequired: false, transformFunction: null }, recreateDemo: { classPropertyName: "recreateDemo", publicName: "recreateDemo", isSignal: true, isRequired: false, transformFunction: null }, form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, expanded: { classPropertyName: "expanded", publicName: "expanded", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "demoOutlet", first: true, predicate: ["demoOutlet"], descendants: true, read: ViewContainerRef, isSignal: true }], usesOnChanges: true, ngImport: i0, template: "<ng-doc-demo-displayer\n [code]=\"code()\"\n [border]=\"false\"\n [expanded]=\"expanded()\"\n language=\"angular-html\">\n <ng-container #demoOutlet></ng-container>\n</ng-doc-demo-displayer>\n", styles: [":host{display:flex;flex-direction:column}ng-doc-demo-displayer{flex:1 1 auto}\n"], dependencies: [{ kind: "component", type: NgDocDemoDisplayerComponent, selector: "ng-doc-demo-displayer", inputs: ["codeContent", "code", "language", "container", "border", "expanded"], outputs: ["expandedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
125
159
  }
126
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocPlaygroundDemoComponent, decorators: [{
160
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocPlaygroundDemoComponent, decorators: [{
127
161
  type: Component,
128
- args: [{ selector: 'ng-doc-playground-demo', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgDocDemoDisplayerComponent], template: "<ng-doc-demo-displayer [code]=\"code\" [border]=\"false\" [expanded]=\"expanded\" language=\"angular-html\">\n <ng-container #demoOutlet></ng-container>\n</ng-doc-demo-displayer>\n", styles: [":host{display:block;border:var(--ng-doc-demo-displayer-border);border-radius:var(--ng-doc-demo-displayer-border-radius);overflow:hidden}:host:not(:last-child){border-bottom:0;border-bottom-left-radius:0;border-bottom-right-radius:0}:host:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}\n"] }]
129
- }], propDecorators: { id: [{
130
- type: Input
131
- }], pipeName: [{
132
- type: Input
133
- }], selector: [{
134
- type: Input
135
- }], configuration: [{
136
- type: Input
137
- }], properties: [{
138
- type: Input
139
- }], recreateDemo: [{
140
- type: Input
141
- }], form: [{
142
- type: Input
143
- }], expanded: [{
144
- type: Input
145
- }], demoOutlet: [{
146
- type: ViewChild,
147
- args: ['demoOutlet', { static: true, read: ViewContainerRef }]
148
- }] } });
162
+ args: [{ selector: 'ng-doc-playground-demo', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgDocDemoDisplayerComponent], template: "<ng-doc-demo-displayer\n [code]=\"code()\"\n [border]=\"false\"\n [expanded]=\"expanded()\"\n language=\"angular-html\">\n <ng-container #demoOutlet></ng-container>\n</ng-doc-demo-displayer>\n", styles: [":host{display:flex;flex-direction:column}ng-doc-demo-displayer{flex:1 1 auto}\n"] }]
163
+ }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], pipeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "pipeName", required: false }] }], selector: [{ type: i0.Input, args: [{ isSignal: true, alias: "selector", required: false }] }], configuration: [{ type: i0.Input, args: [{ isSignal: true, alias: "configuration", required: false }] }], properties: [{ type: i0.Input, args: [{ isSignal: true, alias: "properties", required: false }] }], recreateDemo: [{ type: i0.Input, args: [{ isSignal: true, alias: "recreateDemo", required: false }] }], form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], expanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "expanded", required: false }] }], demoOutlet: [{ type: i0.ViewChild, args: ['demoOutlet', { ...{
164
+ read: ViewContainerRef,
165
+ }, isSignal: true }] }] } });
149
166
 
167
+ /** Value types that have a chip colour. */
168
+ const CHIP_TYPES = new Set([
169
+ 'string',
170
+ 'number',
171
+ 'boolean',
172
+ 'object',
173
+ 'null',
174
+ 'undefined',
175
+ ]);
176
+ /**
177
+ * One row of the playground inspector: the input's name with its type chip, and the type
178
+ * control that edits it, bound to the input's form control.
179
+ */
150
180
  class NgDocPlaygroundPropertyComponent {
151
181
  constructor() {
152
- this.name = '';
153
- }
154
- ngOnChanges({ property, control, typeControl, defaultValue }) {
155
- if ((property || control || typeControl || defaultValue) && this.property && this.typeControl) {
156
- this.propertyTypeControl?.destroy();
157
- this.propertyTypeControl = undefined;
158
- if (this.typeControl && this.propertyOutlet) {
159
- this.propertyTypeControl = this.propertyOutlet.createComponent(this.typeControl.control);
160
- this.propertyTypeControl.instance.name = this.name;
161
- this.propertyTypeControl.instance.description = this.tooltipContent;
162
- this.propertyTypeControl.instance.options = isPlaygroundProperty(this.property)
163
- ? this.property.options
164
- : undefined;
165
- this.propertyTypeControl.instance.default = this.defaultValue;
166
- this.propertyTypeControl.instance.isManual = isPlaygroundProperty(this.property)
167
- ? this.property.isManual
168
- : undefined;
169
- this.propertyTypeControl.instance.writeValue(this.control?.value);
170
- this.option = this.typeControl.options;
171
- }
172
- if (this.control) {
173
- this.control?.registerOnChange((value) => this.propertyTypeControl?.instance?.writeValue(value));
174
- this.propertyTypeControl?.instance.registerOnChange((value) => this.control?.setValue(value));
175
- this.propertyTypeControl?.instance.registerOnTouched(() => this.control?.markAsTouched());
176
- }
182
+ /** Name of the input. */
183
+ this.name = input('', /* @ts-ignore */
184
+ ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
185
+ /** The input, or the content slot. */
186
+ this.property = input(undefined, /* @ts-ignore */
187
+ ...(ngDevMode ? [{ debugName: "property" }] : /* istanbul ignore next */ []));
188
+ /** The type control that edits the input. */
189
+ this.typeControl = input(undefined, /* @ts-ignore */
190
+ ...(ngDevMode ? [{ debugName: "typeControl" }] : /* istanbul ignore next */ []));
191
+ /** Form control of the input. */
192
+ this.control = input(undefined, /* @ts-ignore */
193
+ ...(ngDevMode ? [{ debugName: "control" }] : /* istanbul ignore next */ []));
194
+ /** Default value of the input. */
195
+ this.defaultValue = input(undefined, /* @ts-ignore */
196
+ ...(ngDevMode ? [{ debugName: "defaultValue" }] : /* istanbul ignore next */ []));
197
+ /** Whether a type control is rendered. */
198
+ this.hasPropertyControl = signal(false, /* @ts-ignore */
199
+ ...(ngDevMode ? [{ debugName: "hasPropertyControl" }] : /* istanbul ignore next */ []));
200
+ /** Description of the input, shown in the tooltip of its name. */
201
+ this.tooltipContent = computed(() => {
202
+ const property = this.property();
203
+ return property && isPlaygroundProperty(property) ? property.description ?? '' : '';
204
+ }, /* @ts-ignore */
205
+ ...(ngDevMode ? [{ debugName: "tooltipContent" }] : /* istanbul ignore next */ []));
206
+ /** Options of the type control provider. */
207
+ this.option = computed(() => this.typeControl()?.options, /* @ts-ignore */
208
+ ...(ngDevMode ? [{ debugName: "option" }] : /* istanbul ignore next */ []));
209
+ /** The value type shown as a chip under the name, or an empty string. */
210
+ this.typeChip = computed(() => valueType(this.property()), /* @ts-ignore */
211
+ ...(ngDevMode ? [{ debugName: "typeChip" }] : /* istanbul ignore next */ []));
212
+ this.propertyOutlet = viewChild.required('propertyOutlet', { ...(ngDevMode ? { debugName: "propertyOutlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
213
+ // A plain effect, not an after-render hook: it runs during change detection on the server
214
+ // too, so server-rendered and prerendered pages include the controls. The outlet is a static
215
+ // node of this view, so it exists by then. It runs again only when an input it reads changes.
216
+ effect(() => {
217
+ const property = this.property();
218
+ const typeControl = this.typeControl();
219
+ const control = this.control();
220
+ const defaultValue = this.defaultValue();
221
+ untracked(() => this.createControl(property, typeControl, control, defaultValue));
222
+ });
223
+ inject(DestroyRef).onDestroy(() => this.propertyTypeControl?.destroy());
224
+ }
225
+ createControl(property, typeControl, formControl, defaultValue) {
226
+ if (!property || !typeControl) {
227
+ return;
177
228
  }
229
+ this.propertyTypeControl?.destroy();
230
+ const control = this.propertyOutlet().createComponent(typeControl.control);
231
+ this.propertyTypeControl = control;
232
+ setTypeControlField(control, 'name', this.name());
233
+ setTypeControlField(control, 'description', this.tooltipContent());
234
+ setTypeControlField(control, 'options', isPlaygroundProperty(property) ? property.options : undefined);
235
+ setTypeControlField(control, 'default', defaultValue);
236
+ setTypeControlField(control, 'isManual', isPlaygroundProperty(property) ? property.isManual : undefined);
237
+ control.instance.writeValue(formControl?.value);
238
+ if (formControl) {
239
+ formControl.registerOnChange((value) => control.instance.writeValue(value));
240
+ control.instance.registerOnChange((value) => formControl.setValue(value));
241
+ control.instance.registerOnTouched(() => formControl.markAsTouched());
242
+ }
243
+ this.hasPropertyControl.set(true);
178
244
  }
179
- get hasPropertyControl() {
180
- return !!this.propertyTypeControl;
245
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocPlaygroundPropertyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
246
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.1", type: NgDocPlaygroundPropertyComponent, isStandalone: true, selector: "ng-doc-playground-property", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, property: { classPropertyName: "property", publicName: "property", isSignal: true, isRequired: false, transformFunction: null }, typeControl: { classPropertyName: "typeControl", publicName: "typeControl", isSignal: true, isRequired: false, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: false, transformFunction: null }, defaultValue: { classPropertyName: "defaultValue", publicName: "defaultValue", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-has-property-control": "hasPropertyControl()" } }, viewQueries: [{ propertyName: "propertyOutlet", first: true, predicate: ["propertyOutlet"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: "<label\n class=\"ng-doc-playground-property-label\"\n [class.with-label]=\"!option()?.hideLabel\"\n [ng-doc-label]=\"option()?.hideLabel ? '' : labelContent\">\n <ng-template #labelContent>\n <span\n class=\"ng-doc-playground-property-name\"\n [ngDocTooltip]=\"tooltipTemplate\"\n [canOpen]=\"!!tooltipContent()\"\n [positions]=\"['left-center', 'top-right', 'bottom-right']\"\n >{{ name() }}</span\n >\n @if (typeChip(); as type) {\n <ng-doc-kind-icon [kind]=\"type\" type=\"type\"></ng-doc-kind-icon>\n }\n <ng-template #tooltipTemplate>\n <div [innerHTML]=\"tooltipContent() | ngDocSanitizeHtml\"></div>\n </ng-template>\n </ng-template>\n <ng-container #propertyOutlet></ng-container>\n</label>\n", styles: [":host{display:block}:host[data-has-property-control=false]{display:none}.ng-doc-playground-property-label.with-label{display:grid;grid-template-columns:84px minmax(0,1fr);align-items:center;gap:var(--ng-doc-space-3)}.ng-doc-playground-property-label.with-label ::ng-deep>.ng-doc-label{display:grid;justify-items:start;gap:var(--ng-doc-space-1);min-width:0;margin:0}.ng-doc-playground-property-label.with-label ::ng-deep>.ng-doc-content{width:100%;min-width:0}.ng-doc-playground-property-name{max-width:100%;overflow:hidden;text-overflow:ellipsis;font-family:var(--ng-doc-code-font);font-size:13px;line-height:18px;font-weight:500;color:var(--ng-doc-heading-color)}\n"], dependencies: [{ kind: "component", type: NgDocLabelComponent, selector: "label[ng-doc-label]", inputs: ["ng-doc-label", "align"] }, { kind: "directive", type: NgDocTooltipDirective, selector: "[ngDocTooltip]", inputs: ["ngDocTooltip", "delay", "displayOrigin", "pointerOrigin", "positions", "canOpen", "panelClass", "minHeight", "maxHeight", "height", "minWidth", "maxWidth", "width"], outputs: ["beforeOpen", "afterOpen", "beforeClose", "afterClose"], exportAs: ["ngDocTooltip"] }, { kind: "component", type: NgDocKindIconComponent, selector: "ng-doc-kind-icon", inputs: ["kind", "type", "size"] }, { kind: "pipe", type: NgDocSanitizeHtmlPipe, name: "ngDocSanitizeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
247
+ }
248
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocPlaygroundPropertyComponent, decorators: [{
249
+ type: Component,
250
+ args: [{ selector: 'ng-doc-playground-property', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
251
+ NgDocLabelComponent,
252
+ NgDocTooltipDirective,
253
+ NgDocSanitizeHtmlPipe,
254
+ NgDocKindIconComponent,
255
+ ], host: {
256
+ '[attr.data-has-property-control]': 'hasPropertyControl()',
257
+ }, template: "<label\n class=\"ng-doc-playground-property-label\"\n [class.with-label]=\"!option()?.hideLabel\"\n [ng-doc-label]=\"option()?.hideLabel ? '' : labelContent\">\n <ng-template #labelContent>\n <span\n class=\"ng-doc-playground-property-name\"\n [ngDocTooltip]=\"tooltipTemplate\"\n [canOpen]=\"!!tooltipContent()\"\n [positions]=\"['left-center', 'top-right', 'bottom-right']\"\n >{{ name() }}</span\n >\n @if (typeChip(); as type) {\n <ng-doc-kind-icon [kind]=\"type\" type=\"type\"></ng-doc-kind-icon>\n }\n <ng-template #tooltipTemplate>\n <div [innerHTML]=\"tooltipContent() | ngDocSanitizeHtml\"></div>\n </ng-template>\n </ng-template>\n <ng-container #propertyOutlet></ng-container>\n</label>\n", styles: [":host{display:block}:host[data-has-property-control=false]{display:none}.ng-doc-playground-property-label.with-label{display:grid;grid-template-columns:84px minmax(0,1fr);align-items:center;gap:var(--ng-doc-space-3)}.ng-doc-playground-property-label.with-label ::ng-deep>.ng-doc-label{display:grid;justify-items:start;gap:var(--ng-doc-space-1);min-width:0;margin:0}.ng-doc-playground-property-label.with-label ::ng-deep>.ng-doc-content{width:100%;min-width:0}.ng-doc-playground-property-name{max-width:100%;overflow:hidden;text-overflow:ellipsis;font-family:var(--ng-doc-code-font);font-size:13px;line-height:18px;font-weight:500;color:var(--ng-doc-heading-color)}\n"] }]
258
+ }], ctorParameters: () => [], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], property: [{ type: i0.Input, args: [{ isSignal: true, alias: "property", required: false }] }], typeControl: [{ type: i0.Input, args: [{ isSignal: true, alias: "typeControl", required: false }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], defaultValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValue", required: false }] }], propertyOutlet: [{ type: i0.ViewChild, args: ['propertyOutlet', { ...{
259
+ read: ViewContainerRef,
260
+ }, isSignal: true }] }] } });
261
+ /**
262
+ * The value type of an input: its declared type when it is a primitive, else the type of the first
263
+ * value of its type alias. Content slots are booleans.
264
+ * @param property - The input or the content slot.
265
+ */
266
+ function valueType(property) {
267
+ if (!property) {
268
+ return '';
269
+ }
270
+ if (!isPlaygroundProperty(property)) {
271
+ return 'boolean';
181
272
  }
182
- get tooltipContent() {
183
- return this.property && isPlaygroundProperty(this.property)
184
- ? this.property.description ?? ''
185
- : '';
273
+ if (CHIP_TYPES.has(property.type)) {
274
+ return property.type;
275
+ }
276
+ const option = property.options?.[0];
277
+ if (option !== undefined) {
278
+ try {
279
+ const type = typeof (property.isManual ? option : extractValueOrThrow(option));
280
+ return CHIP_TYPES.has(type) ? type : '';
281
+ }
282
+ catch {
283
+ return '';
284
+ }
285
+ }
286
+ return '';
287
+ }
288
+ /**
289
+ * Sets a field of a type control: through `ComponentRef.setInput()` when the control declares it
290
+ * as an input (a signal input or a decorator input), otherwise by assigning the plain field.
291
+ * @param control - The created type control.
292
+ * @param field - Name of the field.
293
+ * @param value - Value to set.
294
+ */
295
+ function setTypeControlField(control, field, value) {
296
+ const input = reflectComponentType(control.componentType)?.inputs.find(({ propName }) => propName === field);
297
+ if (input) {
298
+ control.setInput(input.templateName, value);
299
+ }
300
+ else {
301
+ control.instance[field] = value;
186
302
  }
187
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocPlaygroundPropertyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
188
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.0.6", type: NgDocPlaygroundPropertyComponent, isStandalone: true, selector: "ng-doc-playground-property", inputs: { name: "name", property: "property", typeControl: "typeControl", control: "control", defaultValue: "defaultValue" }, host: { properties: { "attr.data-has-property-control": "this.hasPropertyControl" } }, viewQueries: [{ propertyName: "propertyOutlet", first: true, predicate: ["propertyOutlet"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: "<label [ng-doc-label]=\"option?.hideLabel ? '' : labelContent\">\n\t<ng-template #labelContent>\n\t\t<span\n\t\t\t[ngDocTooltip]=\"tooltipTemplate\"\n\t\t\t[canOpen]=\"!!tooltipContent\"\n\t\t\t[positions]=\"['left-center', 'top-right', 'bottom-right']\"\n\t\t\t>{{ name }}</span\n\t\t>\n\t\t<ng-template #tooltipTemplate>\n\t\t\t<div [innerHTML]=\"tooltipContent | ngDocSanitizeHtml\"></div>\n\t\t</ng-template>\n\t</ng-template>\n\t<ng-container #propertyOutlet></ng-container>\n</label>\n", styles: [":host{display:block}:host[data-has-property-control=false]{display:none}\n"], dependencies: [{ kind: "component", type: NgDocLabelComponent, selector: "label[ng-doc-label]", inputs: ["ng-doc-label", "align"] }, { kind: "directive", type: NgDocTooltipDirective, selector: "[ngDocTooltip]", inputs: ["ngDocTooltip", "delay", "displayOrigin", "pointerOrigin", "positions", "canOpen", "panelClass", "minHeight", "maxHeight", "height", "minWidth", "maxWidth", "width"], outputs: ["beforeOpen", "afterOpen", "beforeClose", "afterClose"], exportAs: ["ngDocTooltip"] }, { kind: "pipe", type: NgDocSanitizeHtmlPipe, name: "ngDocSanitizeHtml" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
189
303
  }
190
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocPlaygroundPropertyComponent, decorators: [{
191
- type: Component,
192
- args: [{ selector: 'ng-doc-playground-property', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgDocLabelComponent, NgDocTooltipDirective, NgDocSanitizeHtmlPipe], template: "<label [ng-doc-label]=\"option?.hideLabel ? '' : labelContent\">\n\t<ng-template #labelContent>\n\t\t<span\n\t\t\t[ngDocTooltip]=\"tooltipTemplate\"\n\t\t\t[canOpen]=\"!!tooltipContent\"\n\t\t\t[positions]=\"['left-center', 'top-right', 'bottom-right']\"\n\t\t\t>{{ name }}</span\n\t\t>\n\t\t<ng-template #tooltipTemplate>\n\t\t\t<div [innerHTML]=\"tooltipContent | ngDocSanitizeHtml\"></div>\n\t\t</ng-template>\n\t</ng-template>\n\t<ng-container #propertyOutlet></ng-container>\n</label>\n", styles: [":host{display:block}:host[data-has-property-control=false]{display:none}\n"] }]
193
- }], propDecorators: { name: [{
194
- type: Input
195
- }], property: [{
196
- type: Input
197
- }], typeControl: [{
198
- type: Input
199
- }], control: [{
200
- type: Input
201
- }], defaultValue: [{
202
- type: Input
203
- }], propertyOutlet: [{
204
- type: ViewChild,
205
- args: ['propertyOutlet', { read: ViewContainerRef, static: true }]
206
- }], hasPropertyControl: [{
207
- type: HostBinding,
208
- args: ['attr.data-has-property-control']
209
- }] } });
210
304
 
305
+ /**
306
+ * Orders inputs without an `order` by name in English whatever the locale, so a prerendered
307
+ * playground does not depend on the machine that built it.
308
+ */
309
+ const INPUT_ORDER = new Intl.Collator('en');
310
+ /**
311
+ * The playground inspector: the demos, and beside them (below them in a container narrower than
312
+ * 640px) the Recreate setting and a control per input and content slot.
313
+ */
211
314
  class NgDocPlaygroundPropertiesComponent {
212
315
  constructor() {
213
- this.breakpointObserver = inject(BreakpointObserver);
214
316
  this.injector = inject(Injector);
215
- this.ignoreInputs = [];
216
- this.hideSidePanel = false;
217
- this.recreateDemo = false;
218
- this.showResetButton = false;
219
- this.recreateDemoChange = new EventEmitter();
220
- this.resetForm = new EventEmitter();
221
- this.breakpoints = [Breakpoints.XSmall];
222
- this.propertyControls = [];
223
- this.contentTypeControl = this.getControlForType('boolean');
224
- this.observer = this.breakpointObserver
225
- .observe(this.breakpoints)
226
- .pipe(map((state) => state.matches));
227
- }
228
- ngOnChanges({ properties }) {
229
- if (properties && this.properties) {
230
- this.propertyControls = objectKeys(this.properties)
231
- .filter((key) => this.ignoreInputs?.includes(String(key)) !== true)
317
+ /** The form of the playground. */
318
+ this.form = input.required(/* @ts-ignore */
319
+ ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
320
+ /** The inputs of the playground's target. */
321
+ this.properties = input(undefined, /* @ts-ignore */
322
+ ...(ngDevMode ? [{ debugName: "properties" }] : /* istanbul ignore next */ []));
323
+ /** Inputs that get no control. */
324
+ this.ignoreInputs = input([], /* @ts-ignore */
325
+ ...(ngDevMode ? [{ debugName: "ignoreInputs" }] : /* istanbul ignore next */ []));
326
+ /** Content slots of the playground. */
327
+ this.dynamicContent = input(undefined, /* @ts-ignore */
328
+ ...(ngDevMode ? [{ debugName: "dynamicContent" }] : /* istanbul ignore next */ []));
329
+ /** Default values of the inputs; the inspector renders once they are known. */
330
+ this.defaultValues = input(undefined, /* @ts-ignore */
331
+ ...(ngDevMode ? [{ debugName: "defaultValues" }] : /* istanbul ignore next */ []));
332
+ /** Whether the inspector is hidden, leaving the demos only. */
333
+ this.hideSidePanel = input(false, /* @ts-ignore */
334
+ ...(ngDevMode ? [{ debugName: "hideSidePanel" }] : /* istanbul ignore next */ []));
335
+ /** Where the inspector goes: right of the demos, or below them at full width. */
336
+ this.inspectorPosition = input('right', /* @ts-ignore */
337
+ ...(ngDevMode ? [{ debugName: "inspectorPosition" }] : /* istanbul ignore next */ []));
338
+ /** Whether the demo is recreated each time an input changes. */
339
+ this.recreateDemo = model(false, /* @ts-ignore */
340
+ ...(ngDevMode ? [{ debugName: "recreateDemo" }] : /* istanbul ignore next */ []));
341
+ /** Whether the Reset button is shown. */
342
+ this.showResetButton = input(false, /* @ts-ignore */
343
+ ...(ngDevMode ? [{ debugName: "showResetButton" }] : /* istanbul ignore next */ []));
344
+ /** Emits when the reader resets the form. */
345
+ this.resetForm = output();
346
+ /** The control of each input, in display order. */
347
+ this.propertyControls = computed(() => {
348
+ const properties = this.properties();
349
+ const ignoreInputs = this.ignoreInputs();
350
+ if (!properties) {
351
+ return [];
352
+ }
353
+ return objectKeys(properties)
354
+ .filter((key) => ignoreInputs?.includes(String(key)) !== true)
232
355
  .map((key) => {
233
- if (this.properties) {
234
- const property = this.properties[key];
235
- const typeControl = this.getTypeControl(property);
236
- if (typeControl) {
237
- return {
238
- propertyName: String(key),
239
- property,
240
- typeControl,
241
- };
242
- }
243
- }
244
- return null;
356
+ const property = properties[key];
357
+ const typeControl = this.getTypeControl(property);
358
+ return typeControl ? { propertyName: String(key), property, typeControl } : null;
245
359
  })
246
360
  .filter(isPresent)
247
361
  .sort((a, b) => {
@@ -256,16 +370,26 @@ class NgDocPlaygroundPropertiesComponent {
256
370
  if (isPresent(bOrder)) {
257
371
  return 1;
258
372
  }
259
- return a.property.inputName.localeCompare(b.property.inputName);
373
+ return INPUT_ORDER.compare(a.property.inputName, b.property.inputName);
260
374
  });
261
- }
375
+ }, /* @ts-ignore */
376
+ ...(ngDevMode ? [{ debugName: "propertyControls" }] : /* istanbul ignore next */ []));
377
+ /** The control of content slots. */
378
+ this.contentTypeControl = this.getControlForType('boolean');
262
379
  }
380
+ /**
381
+ * The form control of an input or a content slot.
382
+ * @param controlType - `properties` or `content`.
383
+ * @param key - Name of the input or slot.
384
+ */
263
385
  getFormControl(controlType, key) {
264
- return this.form.get(controlType)?.get(key);
386
+ return this.form().get(controlType)?.get(key);
265
387
  }
266
388
  getTypeControl(property) {
267
389
  const type = property.type;
390
+ const primitive = optionalPrimitive(type);
268
391
  const typeControl = this.getControlForType(type) ??
392
+ (primitive ? this.getControlForType(primitive) : undefined) ??
269
393
  this.getControlForTypeAlias(isPlaygroundProperty(property) ? property.options : undefined, property.isManual);
270
394
  if (!typeControl && isDevMode()) {
271
395
  console.warn(`NgDocPlayground didn't find the control for the @Input "${property.inputName}", the type "${type}" was not recognized'`);
@@ -295,211 +419,216 @@ class NgDocPlaygroundPropertiesComponent {
295
419
  }
296
420
  return undefined;
297
421
  }
298
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocPlaygroundPropertiesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
299
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: NgDocPlaygroundPropertiesComponent, isStandalone: true, selector: "ng-doc-playground-properties", inputs: { form: "form", properties: "properties", ignoreInputs: "ignoreInputs", dynamicContent: "dynamicContent", defaultValues: "defaultValues", hideSidePanel: "hideSidePanel", recreateDemo: "recreateDemo", showResetButton: "showResetButton" }, outputs: { recreateDemoChange: "recreateDemoChange", resetForm: "resetForm" }, usesOnChanges: true, ngImport: i0, template: "@if (defaultValues) {\n <div class=\"ng-doc-playground-properties-wrapper\" [class.vertical]=\"observer | async\">\n <div class=\"ng-doc-playground-demos\">\n <ng-content></ng-content>\n </div>\n @if (!hideSidePanel) {\n <div class=\"ng-doc-playground-properties\">\n <div class=\"ng-doc-playground-header\">\n <h4 ng-doc-text>Playground</h4>\n @if (showResetButton) {\n <button ng-doc-button color=\"alert\" (click)=\"resetForm.emit()\">Reset</button>\n }\n </div>\n <div class=\"ng-doc-playground-setting\">\n <ng-doc-checkbox\n [(ngModel)]=\"recreateDemo\"\n (ngModelChange)=\"recreateDemoChange.emit($event)\">\n <span\n ng-doc-text\n [ngDocTooltip]=\"'Recreates demo everytime\\none of the input has changed'\"\n [positions]=\"['bottom-right', 'left-center']\">\n Recreate\n <ng-doc-icon icon=\"info\" ngDocTextRight></ng-doc-icon>\n </span>\n </ng-doc-checkbox>\n </div>\n <div class=\"ng-doc-playground-divider\"></div>\n @if (propertyControls.length) {\n <h5 class=\"ng-doc-title\" ng-doc-text>Settings</h5>\n @for (propertyControl of propertyControls; track propertyControl) {\n <ng-doc-playground-property\n [name]=\"propertyControl.property.inputName\"\n [property]=\"propertyControl.property\"\n [typeControl]=\"propertyControl.typeControl\"\n [defaultValue]=\"defaultValues[propertyControl.propertyName]\"\n [control]=\"\n getFormControl | bind: this | execute: 'properties' : propertyControl.propertyName\n \">\n </ng-doc-playground-property>\n }\n }\n @if ((dynamicContent | keyvalue)?.length) {\n <h5 class=\"ng-doc-title\" ng-doc-text>Content</h5>\n @for (content of dynamicContent | keyvalue; track content) {\n <ng-doc-playground-property\n [name]=\"content.value.label\"\n [property]=\"content.value\"\n [typeControl]=\"contentTypeControl\"\n [control]=\"getFormControl | bind: this | execute: 'content' : content.key\">\n </ng-doc-playground-property>\n }\n }\n </div>\n }\n </div>\n}\n", styles: [":host .ng-doc-playground-properties-wrapper{position:relative;display:flex;align-items:flex-start;gap:calc(var(--ng-doc-base-gutter) * 2)}:host .ng-doc-playground-properties-wrapper.vertical{flex-direction:column}:host .ng-doc-playground-properties-wrapper.vertical .ng-doc-playground-demos{margin-right:0;margin-bottom:calc(var(--ng-doc-base-gutter) * 2)}:host .ng-doc-playground-properties-wrapper.vertical .ng-doc-playground-properties{width:100%}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-demos{position:sticky;display:flex;flex-direction:column;width:100%;min-width:0;z-index:4;top:calc(var(--ng-doc-page-sticky-offset) + var(--ng-doc-base-gutter))}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties{flex-shrink:0;align-self:normal;width:300px;padding:calc(var(--ng-doc-base-gutter) * 2) calc(var(--ng-doc-base-gutter) * 3);background-color:var(--ng-doc-base-1);border-radius:var(--ng-doc-base-gutter);border:1px solid var(--ng-doc-border-color)}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties ng-doc-playground-property:not(:last-child){margin-bottom:calc(var(--ng-doc-base-gutter) * 2)}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties .ng-doc-playground-controls{display:flex;justify-content:flex-end}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties .ng-doc-playground-header{display:flex;align-items:center;justify-content:space-between;height:40px;margin-bottom:calc(var(--ng-doc-base-gutter) * 2)}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties .ng-doc-playground-header h4{margin:0}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties .ng-doc-playground-setting{display:flex;flex-direction:column}:host .ng-doc-playground-properties-wrapper .ng-doc-title{margin-top:0;margin-bottom:calc(var(--ng-doc-base-gutter) + var(--ng-doc-base-gutter) / 2)}:host .ng-doc-playground-divider{margin:calc(var(--ng-doc-base-gutter) * 2) 0;height:1px;background-color:var(--ng-doc-base-2)}\n"], dependencies: [{ kind: "component", type: NgDocTextComponent, selector: "[ng-doc-text]", inputs: ["size", "color", "align", "absoluteContent"] }, { kind: "component", type: NgDocButtonComponent, selector: "button[ng-doc-button], a[ng-doc-button], button[ng-doc-button-flat], a[ng-doc-button-flat], button[ng-doc-button-text], a[ng-doc-button-text]", inputs: ["size", "color", "rounded"] }, { kind: "component", type: NgDocCheckboxComponent, selector: "ng-doc-checkbox", inputs: ["color"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgDocTooltipDirective, selector: "[ngDocTooltip]", inputs: ["ngDocTooltip", "delay", "displayOrigin", "pointerOrigin", "positions", "canOpen", "panelClass", "minHeight", "maxHeight", "height", "minWidth", "maxWidth", "width"], outputs: ["beforeOpen", "afterOpen", "beforeClose", "afterClose"], exportAs: ["ngDocTooltip"] }, { kind: "component", type: NgDocIconComponent, selector: "ng-doc-icon", inputs: ["icon", "customIcon", "size"] }, { kind: "directive", type: NgDocTextRightDirective, selector: "[ngDocTextRight]" }, { kind: "component", type: NgDocPlaygroundPropertyComponent, selector: "ng-doc-playground-property", inputs: ["name", "property", "typeControl", "control", "defaultValue"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: NgDocBindPipe, name: "bind" }, { kind: "pipe", type: NgDocExecutePipe, name: "execute" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
422
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocPlaygroundPropertiesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
423
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.1", type: NgDocPlaygroundPropertiesComponent, isStandalone: true, selector: "ng-doc-playground-properties", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: true, transformFunction: null }, properties: { classPropertyName: "properties", publicName: "properties", isSignal: true, isRequired: false, transformFunction: null }, ignoreInputs: { classPropertyName: "ignoreInputs", publicName: "ignoreInputs", isSignal: true, isRequired: false, transformFunction: null }, dynamicContent: { classPropertyName: "dynamicContent", publicName: "dynamicContent", isSignal: true, isRequired: false, transformFunction: null }, defaultValues: { classPropertyName: "defaultValues", publicName: "defaultValues", isSignal: true, isRequired: false, transformFunction: null }, hideSidePanel: { classPropertyName: "hideSidePanel", publicName: "hideSidePanel", isSignal: true, isRequired: false, transformFunction: null }, inspectorPosition: { classPropertyName: "inspectorPosition", publicName: "inspectorPosition", isSignal: true, isRequired: false, transformFunction: null }, recreateDemo: { classPropertyName: "recreateDemo", publicName: "recreateDemo", isSignal: true, isRequired: false, transformFunction: null }, showResetButton: { classPropertyName: "showResetButton", publicName: "showResetButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { recreateDemo: "recreateDemoChange", resetForm: "resetForm" }, ngImport: i0, template: "@if (defaultValues(); as defaultValues) {\n <div\n class=\"ng-doc-playground-properties-wrapper\"\n [class.no-side-panel]=\"hideSidePanel()\"\n [class.inspector-bottom]=\"inspectorPosition() === 'bottom'\">\n <div class=\"ng-doc-playground-demos\">\n <ng-content></ng-content>\n </div>\n @if (!hideSidePanel()) {\n <div class=\"ng-doc-playground-properties\">\n <div class=\"ng-doc-playground-header\">\n <h4>Playground</h4>\n @if (showResetButton()) {\n <button type=\"button\" class=\"ng-doc-playground-reset\" (click)=\"resetForm.emit()\">\n Reset\n </button>\n }\n </div>\n <div class=\"ng-doc-playground-setting\">\n <ng-doc-checkbox [ngModel]=\"recreateDemo()\" (ngModelChange)=\"recreateDemo.set($event)\">\n Recreate\n </ng-doc-checkbox>\n <ng-doc-icon\n class=\"ng-doc-playground-info\"\n icon=\"info\"\n role=\"img\"\n aria-label=\"Recreates the demo every time one of the inputs changes\"\n [ngDocTooltip]=\"'Recreates the demo every time one of the inputs changes'\"\n [positions]=\"['bottom-right', 'left-center']\"></ng-doc-icon>\n </div>\n <div class=\"ng-doc-playground-divider\"></div>\n @if (propertyControls().length) {\n <h5 class=\"ng-doc-title\">Settings</h5>\n <div class=\"ng-doc-playground-property-list\">\n @for (propertyControl of propertyControls(); track propertyControl.propertyName) {\n <ng-doc-playground-property\n [name]=\"propertyControl.property.inputName\"\n [property]=\"propertyControl.property\"\n [typeControl]=\"propertyControl.typeControl\"\n [defaultValue]=\"defaultValues[propertyControl.propertyName]\"\n [control]=\"\n getFormControl | bind: this | execute: 'properties' : propertyControl.propertyName\n \">\n </ng-doc-playground-property>\n }\n </div>\n }\n @if ((dynamicContent() | keyvalue)?.length) {\n <h5 class=\"ng-doc-title\">Content</h5>\n <div class=\"ng-doc-playground-property-list\">\n @for (content of dynamicContent() | keyvalue; track content.key) {\n <ng-doc-playground-property\n [name]=\"content.value.label\"\n [property]=\"content.value\"\n [typeControl]=\"contentTypeControl\"\n [control]=\"getFormControl | bind: this | execute: 'content' : content.key\">\n </ng-doc-playground-property>\n }\n </div>\n }\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;container:ng-doc-playground/inline-size}.ng-doc-playground-properties-wrapper{display:grid;grid-template-columns:minmax(0,1fr) var(--ng-doc-playground-inspector-width);align-items:stretch;border:var(--ng-doc-demo-displayer-border);border-radius:var(--ng-doc-demo-displayer-border-radius);overflow:clip;background-color:var(--ng-doc-demo-displayer-background, var(--ng-doc-background))}.ng-doc-playground-properties-wrapper.no-side-panel{grid-template-columns:minmax(0,1fr)}.ng-doc-playground-properties-wrapper:not(.no-side-panel):not(.inspector-bottom) .ng-doc-playground-demos{position:sticky;top:calc(var(--ng-doc-page-sticky-offset) + var(--ng-doc-base-gutter));align-self:start;z-index:4}.ng-doc-playground-demos{display:flex;flex-direction:column;min-width:0}.ng-doc-playground-properties-wrapper:not(.no-side-panel):not(.inspector-bottom) .ng-doc-playground-demos{min-height:min(100%,100dvh - var(--ng-doc-page-sticky-offset) - 2 * var(--ng-doc-base-gutter))}.ng-doc-playground-properties{min-width:0;background:var(--ng-doc-base-1);border-left:1px solid var(--ng-doc-border-color);font-family:var(--ng-doc-font-family);font-size:14px;line-height:20px;color:var(--ng-doc-text);--ng-doc-input-height: 36px;--ng-doc-input-border: var(--ng-doc-playground-input-border);--ng-doc-checkbox-border: var(--ng-doc-playground-input-border)}.ng-doc-playground-header{display:flex;align-items:center;gap:var(--ng-doc-space-2);height:52px;padding:0 var(--ng-doc-space-3) 0 var(--ng-doc-space-4)}.ng-doc-playground-header h4{margin:0;font-family:var(--ng-doc-heading-font-family);font-size:15px;line-height:22px;font-weight:600;letter-spacing:0;color:var(--ng-doc-heading-color)}.ng-doc-playground-reset{display:inline-flex;align-items:center;height:28px;margin-left:auto;padding:0 10px;border:1px solid var(--ng-doc-border-color);border-radius:var(--ng-doc-radius-md);background:var(--ng-doc-background);font:inherit;font-size:12px;font-weight:500;color:var(--ng-doc-heading-color);cursor:pointer}.ng-doc-playground-reset:hover{border-color:var(--ng-doc-border-hover-color)}.ng-doc-playground-reset:focus-visible{outline:none;box-shadow:var(--ng-doc-focus-ring)}.ng-doc-playground-setting{display:flex;align-items:center;gap:6px;padding:0 var(--ng-doc-space-4) var(--ng-doc-space-3)}.ng-doc-playground-info{--ng-doc-icon-color: var(--ng-doc-text-muted)}.ng-doc-playground-divider{height:1px;background:var(--ng-doc-border-color)}h5.ng-doc-title{margin:0;padding:var(--ng-doc-space-3) var(--ng-doc-space-4) var(--ng-doc-space-1);font-family:var(--ng-doc-font-family);font-size:11px;line-height:16px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--ng-doc-text-muted)}ng-doc-playground-property{padding:var(--ng-doc-space-2) var(--ng-doc-space-4)}ng-doc-playground-property+ng-doc-playground-property{border-top:1px solid var(--ng-doc-base-2)}.ng-doc-playground-property-list:last-child{padding-bottom:var(--ng-doc-space-2)}.ng-doc-playground-properties-wrapper.inspector-bottom{grid-template-columns:minmax(0,1fr)}.ng-doc-playground-properties-wrapper.inspector-bottom .ng-doc-playground-properties{border-left:0;border-top:1px solid var(--ng-doc-border-color)}.ng-doc-playground-properties-wrapper.inspector-bottom .ng-doc-playground-property-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,320px),1fr));column-gap:var(--ng-doc-space-4)}.ng-doc-playground-properties-wrapper.inspector-bottom ng-doc-playground-property+ng-doc-playground-property{border-top:0}@container ng-doc-playground (max-width: 640px){.ng-doc-playground-properties-wrapper{grid-template-columns:minmax(0,1fr)}.ng-doc-playground-properties-wrapper:not(.no-side-panel) .ng-doc-playground-demos{position:static}.ng-doc-playground-properties{border-left:0;border-top:1px solid var(--ng-doc-border-color)}}\n"], dependencies: [{ kind: "component", type: NgDocCheckboxComponent, selector: "ng-doc-checkbox", inputs: ["color"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgDocTooltipDirective, selector: "[ngDocTooltip]", inputs: ["ngDocTooltip", "delay", "displayOrigin", "pointerOrigin", "positions", "canOpen", "panelClass", "minHeight", "maxHeight", "height", "minWidth", "maxWidth", "width"], outputs: ["beforeOpen", "afterOpen", "beforeClose", "afterClose"], exportAs: ["ngDocTooltip"] }, { kind: "component", type: NgDocIconComponent, selector: "ng-doc-icon", inputs: ["icon", "customIcon", "size"] }, { kind: "component", type: NgDocPlaygroundPropertyComponent, selector: "ng-doc-playground-property", inputs: ["name", "property", "typeControl", "control", "defaultValue"] }, { kind: "pipe", type: KeyValuePipe, name: "keyvalue" }, { kind: "pipe", type: NgDocBindPipe, name: "bind" }, { kind: "pipe", type: NgDocExecutePipe, name: "execute" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
300
424
  }
301
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocPlaygroundPropertiesComponent, decorators: [{
425
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocPlaygroundPropertiesComponent, decorators: [{
302
426
  type: Component,
303
427
  args: [{ selector: 'ng-doc-playground-properties', changeDetection: ChangeDetectionStrategy.OnPush, imports: [
304
- NgDocTextComponent,
305
- NgDocButtonComponent,
306
428
  NgDocCheckboxComponent,
307
429
  FormsModule,
308
430
  NgDocTooltipDirective,
309
431
  NgDocIconComponent,
310
- NgDocTextRightDirective,
311
432
  NgDocPlaygroundPropertyComponent,
312
- AsyncPipe,
313
433
  KeyValuePipe,
314
434
  NgDocBindPipe,
315
435
  NgDocExecutePipe,
316
- ], template: "@if (defaultValues) {\n <div class=\"ng-doc-playground-properties-wrapper\" [class.vertical]=\"observer | async\">\n <div class=\"ng-doc-playground-demos\">\n <ng-content></ng-content>\n </div>\n @if (!hideSidePanel) {\n <div class=\"ng-doc-playground-properties\">\n <div class=\"ng-doc-playground-header\">\n <h4 ng-doc-text>Playground</h4>\n @if (showResetButton) {\n <button ng-doc-button color=\"alert\" (click)=\"resetForm.emit()\">Reset</button>\n }\n </div>\n <div class=\"ng-doc-playground-setting\">\n <ng-doc-checkbox\n [(ngModel)]=\"recreateDemo\"\n (ngModelChange)=\"recreateDemoChange.emit($event)\">\n <span\n ng-doc-text\n [ngDocTooltip]=\"'Recreates demo everytime\\none of the input has changed'\"\n [positions]=\"['bottom-right', 'left-center']\">\n Recreate\n <ng-doc-icon icon=\"info\" ngDocTextRight></ng-doc-icon>\n </span>\n </ng-doc-checkbox>\n </div>\n <div class=\"ng-doc-playground-divider\"></div>\n @if (propertyControls.length) {\n <h5 class=\"ng-doc-title\" ng-doc-text>Settings</h5>\n @for (propertyControl of propertyControls; track propertyControl) {\n <ng-doc-playground-property\n [name]=\"propertyControl.property.inputName\"\n [property]=\"propertyControl.property\"\n [typeControl]=\"propertyControl.typeControl\"\n [defaultValue]=\"defaultValues[propertyControl.propertyName]\"\n [control]=\"\n getFormControl | bind: this | execute: 'properties' : propertyControl.propertyName\n \">\n </ng-doc-playground-property>\n }\n }\n @if ((dynamicContent | keyvalue)?.length) {\n <h5 class=\"ng-doc-title\" ng-doc-text>Content</h5>\n @for (content of dynamicContent | keyvalue; track content) {\n <ng-doc-playground-property\n [name]=\"content.value.label\"\n [property]=\"content.value\"\n [typeControl]=\"contentTypeControl\"\n [control]=\"getFormControl | bind: this | execute: 'content' : content.key\">\n </ng-doc-playground-property>\n }\n }\n </div>\n }\n </div>\n}\n", styles: [":host .ng-doc-playground-properties-wrapper{position:relative;display:flex;align-items:flex-start;gap:calc(var(--ng-doc-base-gutter) * 2)}:host .ng-doc-playground-properties-wrapper.vertical{flex-direction:column}:host .ng-doc-playground-properties-wrapper.vertical .ng-doc-playground-demos{margin-right:0;margin-bottom:calc(var(--ng-doc-base-gutter) * 2)}:host .ng-doc-playground-properties-wrapper.vertical .ng-doc-playground-properties{width:100%}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-demos{position:sticky;display:flex;flex-direction:column;width:100%;min-width:0;z-index:4;top:calc(var(--ng-doc-page-sticky-offset) + var(--ng-doc-base-gutter))}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties{flex-shrink:0;align-self:normal;width:300px;padding:calc(var(--ng-doc-base-gutter) * 2) calc(var(--ng-doc-base-gutter) * 3);background-color:var(--ng-doc-base-1);border-radius:var(--ng-doc-base-gutter);border:1px solid var(--ng-doc-border-color)}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties ng-doc-playground-property:not(:last-child){margin-bottom:calc(var(--ng-doc-base-gutter) * 2)}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties .ng-doc-playground-controls{display:flex;justify-content:flex-end}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties .ng-doc-playground-header{display:flex;align-items:center;justify-content:space-between;height:40px;margin-bottom:calc(var(--ng-doc-base-gutter) * 2)}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties .ng-doc-playground-header h4{margin:0}:host .ng-doc-playground-properties-wrapper .ng-doc-playground-properties .ng-doc-playground-setting{display:flex;flex-direction:column}:host .ng-doc-playground-properties-wrapper .ng-doc-title{margin-top:0;margin-bottom:calc(var(--ng-doc-base-gutter) + var(--ng-doc-base-gutter) / 2)}:host .ng-doc-playground-divider{margin:calc(var(--ng-doc-base-gutter) * 2) 0;height:1px;background-color:var(--ng-doc-base-2)}\n"] }]
317
- }], ctorParameters: () => [], propDecorators: { form: [{
318
- type: Input
319
- }], properties: [{
320
- type: Input
321
- }], ignoreInputs: [{
322
- type: Input
323
- }], dynamicContent: [{
324
- type: Input
325
- }], defaultValues: [{
326
- type: Input
327
- }], hideSidePanel: [{
328
- type: Input
329
- }], recreateDemo: [{
330
- type: Input
331
- }], showResetButton: [{
332
- type: Input
333
- }], recreateDemoChange: [{
334
- type: Output
335
- }], resetForm: [{
336
- type: Output
337
- }] } });
436
+ ], template: "@if (defaultValues(); as defaultValues) {\n <div\n class=\"ng-doc-playground-properties-wrapper\"\n [class.no-side-panel]=\"hideSidePanel()\"\n [class.inspector-bottom]=\"inspectorPosition() === 'bottom'\">\n <div class=\"ng-doc-playground-demos\">\n <ng-content></ng-content>\n </div>\n @if (!hideSidePanel()) {\n <div class=\"ng-doc-playground-properties\">\n <div class=\"ng-doc-playground-header\">\n <h4>Playground</h4>\n @if (showResetButton()) {\n <button type=\"button\" class=\"ng-doc-playground-reset\" (click)=\"resetForm.emit()\">\n Reset\n </button>\n }\n </div>\n <div class=\"ng-doc-playground-setting\">\n <ng-doc-checkbox [ngModel]=\"recreateDemo()\" (ngModelChange)=\"recreateDemo.set($event)\">\n Recreate\n </ng-doc-checkbox>\n <ng-doc-icon\n class=\"ng-doc-playground-info\"\n icon=\"info\"\n role=\"img\"\n aria-label=\"Recreates the demo every time one of the inputs changes\"\n [ngDocTooltip]=\"'Recreates the demo every time one of the inputs changes'\"\n [positions]=\"['bottom-right', 'left-center']\"></ng-doc-icon>\n </div>\n <div class=\"ng-doc-playground-divider\"></div>\n @if (propertyControls().length) {\n <h5 class=\"ng-doc-title\">Settings</h5>\n <div class=\"ng-doc-playground-property-list\">\n @for (propertyControl of propertyControls(); track propertyControl.propertyName) {\n <ng-doc-playground-property\n [name]=\"propertyControl.property.inputName\"\n [property]=\"propertyControl.property\"\n [typeControl]=\"propertyControl.typeControl\"\n [defaultValue]=\"defaultValues[propertyControl.propertyName]\"\n [control]=\"\n getFormControl | bind: this | execute: 'properties' : propertyControl.propertyName\n \">\n </ng-doc-playground-property>\n }\n </div>\n }\n @if ((dynamicContent() | keyvalue)?.length) {\n <h5 class=\"ng-doc-title\">Content</h5>\n <div class=\"ng-doc-playground-property-list\">\n @for (content of dynamicContent() | keyvalue; track content.key) {\n <ng-doc-playground-property\n [name]=\"content.value.label\"\n [property]=\"content.value\"\n [typeControl]=\"contentTypeControl\"\n [control]=\"getFormControl | bind: this | execute: 'content' : content.key\">\n </ng-doc-playground-property>\n }\n </div>\n }\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;container:ng-doc-playground/inline-size}.ng-doc-playground-properties-wrapper{display:grid;grid-template-columns:minmax(0,1fr) var(--ng-doc-playground-inspector-width);align-items:stretch;border:var(--ng-doc-demo-displayer-border);border-radius:var(--ng-doc-demo-displayer-border-radius);overflow:clip;background-color:var(--ng-doc-demo-displayer-background, var(--ng-doc-background))}.ng-doc-playground-properties-wrapper.no-side-panel{grid-template-columns:minmax(0,1fr)}.ng-doc-playground-properties-wrapper:not(.no-side-panel):not(.inspector-bottom) .ng-doc-playground-demos{position:sticky;top:calc(var(--ng-doc-page-sticky-offset) + var(--ng-doc-base-gutter));align-self:start;z-index:4}.ng-doc-playground-demos{display:flex;flex-direction:column;min-width:0}.ng-doc-playground-properties-wrapper:not(.no-side-panel):not(.inspector-bottom) .ng-doc-playground-demos{min-height:min(100%,100dvh - var(--ng-doc-page-sticky-offset) - 2 * var(--ng-doc-base-gutter))}.ng-doc-playground-properties{min-width:0;background:var(--ng-doc-base-1);border-left:1px solid var(--ng-doc-border-color);font-family:var(--ng-doc-font-family);font-size:14px;line-height:20px;color:var(--ng-doc-text);--ng-doc-input-height: 36px;--ng-doc-input-border: var(--ng-doc-playground-input-border);--ng-doc-checkbox-border: var(--ng-doc-playground-input-border)}.ng-doc-playground-header{display:flex;align-items:center;gap:var(--ng-doc-space-2);height:52px;padding:0 var(--ng-doc-space-3) 0 var(--ng-doc-space-4)}.ng-doc-playground-header h4{margin:0;font-family:var(--ng-doc-heading-font-family);font-size:15px;line-height:22px;font-weight:600;letter-spacing:0;color:var(--ng-doc-heading-color)}.ng-doc-playground-reset{display:inline-flex;align-items:center;height:28px;margin-left:auto;padding:0 10px;border:1px solid var(--ng-doc-border-color);border-radius:var(--ng-doc-radius-md);background:var(--ng-doc-background);font:inherit;font-size:12px;font-weight:500;color:var(--ng-doc-heading-color);cursor:pointer}.ng-doc-playground-reset:hover{border-color:var(--ng-doc-border-hover-color)}.ng-doc-playground-reset:focus-visible{outline:none;box-shadow:var(--ng-doc-focus-ring)}.ng-doc-playground-setting{display:flex;align-items:center;gap:6px;padding:0 var(--ng-doc-space-4) var(--ng-doc-space-3)}.ng-doc-playground-info{--ng-doc-icon-color: var(--ng-doc-text-muted)}.ng-doc-playground-divider{height:1px;background:var(--ng-doc-border-color)}h5.ng-doc-title{margin:0;padding:var(--ng-doc-space-3) var(--ng-doc-space-4) var(--ng-doc-space-1);font-family:var(--ng-doc-font-family);font-size:11px;line-height:16px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;color:var(--ng-doc-text-muted)}ng-doc-playground-property{padding:var(--ng-doc-space-2) var(--ng-doc-space-4)}ng-doc-playground-property+ng-doc-playground-property{border-top:1px solid var(--ng-doc-base-2)}.ng-doc-playground-property-list:last-child{padding-bottom:var(--ng-doc-space-2)}.ng-doc-playground-properties-wrapper.inspector-bottom{grid-template-columns:minmax(0,1fr)}.ng-doc-playground-properties-wrapper.inspector-bottom .ng-doc-playground-properties{border-left:0;border-top:1px solid var(--ng-doc-border-color)}.ng-doc-playground-properties-wrapper.inspector-bottom .ng-doc-playground-property-list{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(100%,320px),1fr));column-gap:var(--ng-doc-space-4)}.ng-doc-playground-properties-wrapper.inspector-bottom ng-doc-playground-property+ng-doc-playground-property{border-top:0}@container ng-doc-playground (max-width: 640px){.ng-doc-playground-properties-wrapper{grid-template-columns:minmax(0,1fr)}.ng-doc-playground-properties-wrapper:not(.no-side-panel) .ng-doc-playground-demos{position:static}.ng-doc-playground-properties{border-left:0;border-top:1px solid var(--ng-doc-border-color)}}\n"] }]
437
+ }], propDecorators: { form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: true }] }], properties: [{ type: i0.Input, args: [{ isSignal: true, alias: "properties", required: false }] }], ignoreInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "ignoreInputs", required: false }] }], dynamicContent: [{ type: i0.Input, args: [{ isSignal: true, alias: "dynamicContent", required: false }] }], defaultValues: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValues", required: false }] }], hideSidePanel: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideSidePanel", required: false }] }], inspectorPosition: [{ type: i0.Input, args: [{ isSignal: true, alias: "inspectorPosition", required: false }] }], recreateDemo: [{ type: i0.Input, args: [{ isSignal: true, alias: "recreateDemo", required: false }] }, { type: i0.Output, args: ["recreateDemoChange"] }], showResetButton: [{ type: i0.Input, args: [{ isSignal: true, alias: "showResetButton", required: false }] }], resetForm: [{ type: i0.Output, args: ["resetForm"] }] } });
438
+ /** The types whose control also edits an optional or nullable input of the type. */
439
+ const PRIMITIVE_TYPES = new Set(['string', 'number', 'boolean']);
440
+ /**
441
+ * The primitive of an optional or nullable primitive input type, such as `string` of
442
+ * `string | undefined` (an optional `@Input() label?: string`, `input<string>()` or
443
+ * `model<string>()`) or of `number | null`, so that the input gets the primitive's control.
444
+ * Other types are matched by their exact name only: a custom control registered for `Position`
445
+ * does not receive the `undefined` of a `Position | undefined` input unless it is registered for
446
+ * that type too.
447
+ * @param type - The input type as the builder printed it.
448
+ * @returns The primitive, or `undefined` when the type is not one.
449
+ */
450
+ function optionalPrimitive(type) {
451
+ const members = type.split('|').map((member) => member.trim());
452
+ const rest = members.filter((member) => member !== 'undefined' && member !== 'null');
453
+ return rest.length === 1 && rest.length < members.length && PRIMITIVE_TYPES.has(rest[0])
454
+ ? rest[0]
455
+ : undefined;
456
+ }
338
457
 
458
+ /**
459
+ * A playground: an inspector with a control per input and content slot of its target, and a demo
460
+ * per selector (or the pipe) rendered with the chosen values.
461
+ */
339
462
  class NgDocPlaygroundComponent {
340
463
  constructor() {
341
464
  this.rootPage = inject(NgDocRootPage);
342
465
  this.formBuilder = inject(FormBuilder);
343
- this.id = '';
344
- this.pipeName = '';
345
- this.selectors = [];
346
- this.options = {};
347
- this.recreateDemo = false;
466
+ /** The name of the playground in the page configuration. */
467
+ this.id = input.required(/* @ts-ignore */
468
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
469
+ /** The name of the pipe, when the target is a pipe. */
470
+ this.pipeName = input('', /* @ts-ignore */
471
+ ...(ngDevMode ? [{ debugName: "pipeName" }] : /* istanbul ignore next */ []));
472
+ /** The selectors of the target to render a demo for. */
473
+ this.selectors = input([], /* @ts-ignore */
474
+ ...(ngDevMode ? [{ debugName: "selectors" }] : /* istanbul ignore next */ []));
475
+ /** The inputs of the target. */
476
+ this.properties = input(undefined, /* @ts-ignore */
477
+ ...(ngDevMode ? [{ debugName: "properties" }] : /* istanbul ignore next */ []));
478
+ /** Options of the playground action; they extend the playground configuration. */
479
+ this.options = input({}, /* @ts-ignore */
480
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
481
+ /** Whether the demo is created again each time a value changes. */
482
+ this.recreateDemo = signal(false, /* @ts-ignore */
483
+ ...(ngDevMode ? [{ debugName: "recreateDemo" }] : /* istanbul ignore next */ []));
484
+ /** The values of the inputs and content slots. */
485
+ this.formGroup = signal(undefined, /* @ts-ignore */
486
+ ...(ngDevMode ? [{ debugName: "formGroup" }] : /* istanbul ignore next */ []));
487
+ /** The default values of the target's inputs, reported by the first demo. */
488
+ this.defaultValues = signal(undefined, /* @ts-ignore */
489
+ ...(ngDevMode ? [{ debugName: "defaultValues" }] : /* istanbul ignore next */ []));
490
+ /** The playground configuration of the page, extended by the options. */
491
+ this.configuration = computed(() => Object.assign({}, this.rootPage.page?.playgrounds?.[this.id()], this.options()), /* @ts-ignore */
492
+ ...(ngDevMode ? [{ debugName: "configuration" }] : /* istanbul ignore next */ []));
348
493
  this.defaultProperties = {};
349
494
  this.defaultContent = {};
350
495
  }
351
- ngOnChanges({ options }) {
352
- if (options) {
353
- // Join configuration with options
354
- this.configuration = Object.assign({}, this.rootPage.page?.playgrounds?.[this.id], this.options);
355
- }
356
- }
357
496
  ngAfterViewInit() {
358
497
  this.defaultProperties = this.getPropertiesFormValues();
359
498
  this.defaultContent = this.getContentFormValues();
360
499
  const propertiesForm = this.formBuilder.group(this.defaultProperties);
361
500
  const contentForm = this.formBuilder.group(this.defaultContent);
362
- this.formGroup = this.formBuilder.group({
501
+ const formGroup = this.formBuilder.group({
363
502
  properties: propertiesForm,
364
503
  content: contentForm,
365
504
  });
366
505
  // `patchValue` is needed to set `undefined` values, otherwise they will be ignored by the Angular form
367
- this.formGroup.patchValue({
368
- properties: Object.assign({}, this.defaultProperties, this.configuration.inputs),
506
+ formGroup.patchValue({
507
+ properties: Object.assign({}, this.defaultProperties, this.configuration().inputs),
369
508
  content: this.defaultContent,
370
509
  });
510
+ this.formGroup.set(formGroup);
371
511
  }
372
512
  isDefaultState() {
373
- if (!this.formGroup) {
513
+ const formGroup = this.formGroup();
514
+ if (!formGroup) {
374
515
  return false;
375
516
  }
376
- return (isSameObject(this.formGroup.value.properties ?? {}, this.defaultValues ?? {}) &&
377
- isSameObject(this.formGroup.value.content ?? {}, this.defaultContent ?? {}));
517
+ return (isSameObject(formGroup.value.properties ?? {}, this.defaultValues() ?? {}) &&
518
+ isSameObject(formGroup.value.content ?? {}, this.defaultContent ?? {}));
378
519
  }
379
520
  getPropertiesFormValues() {
380
- const formValues = objectKeys(this.properties ?? {}).reduce((controls, key) => {
381
- if (this.properties) {
382
- controls[key] = this.defaultValues ? this.defaultValues[key] : undefined;
383
- }
521
+ const defaultValues = this.defaultValues();
522
+ const formValues = objectKeys(this.properties() ?? {}).reduce((controls, key) => {
523
+ controls[key] = defaultValues ? defaultValues[key] : undefined;
384
524
  return controls;
385
525
  }, {});
386
- return Object.assign({}, formValues, this.configuration.defaults);
526
+ return Object.assign({}, formValues, this.configuration().defaults);
387
527
  }
388
528
  getContentFormValues() {
389
- return objectKeys(this.configuration?.content ?? {}).reduce((controls, key) => {
390
- if (this.configuration?.content) {
391
- controls[key] = false;
392
- }
529
+ return objectKeys(this.configuration().content ?? {}).reduce((controls, key) => {
530
+ controls[key] = false;
393
531
  return controls;
394
532
  }, {});
395
533
  }
534
+ /** Sets every input and content slot back to its default value. */
396
535
  resetForm() {
397
- this.formGroup.reset({}, { emitEvent: false });
398
- this.formGroup?.patchValue({
536
+ const formGroup = this.formGroup();
537
+ formGroup?.reset({}, { emitEvent: false });
538
+ formGroup?.patchValue({
399
539
  properties: this.defaultProperties,
400
540
  content: this.defaultContent,
401
541
  });
402
542
  }
403
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocPlaygroundComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
404
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.6", type: NgDocPlaygroundComponent, isStandalone: true, selector: "ng-doc-playground", inputs: { id: "id", pipeName: "pipeName", selectors: "selectors", properties: "properties", options: "options" }, usesOnChanges: true, ngImport: i0, template: "@if (configuration) {\n <ng-doc-playground-properties\n [form]=\"formGroup\"\n [properties]=\"properties\"\n [ignoreInputs]=\"configuration.hiddenInputs\"\n [dynamicContent]=\"configuration.content\"\n [hideSidePanel]=\"configuration.hideSidePanel ?? false\"\n [defaultValues]=\"defaultValues\"\n [showResetButton]=\"!isDefaultState()\"\n [(recreateDemo)]=\"recreateDemo\"\n (resetForm)=\"resetForm()\">\n @for (selector of configuration.selectors ?? selectors | asArray; track selector) {\n <ng-doc-playground-demo\n [id]=\"id\"\n [selector]=\"selector\"\n [properties]=\"properties\"\n [configuration]=\"configuration\"\n [recreateDemo]=\"recreateDemo\"\n [form]=\"formGroup\"\n [expanded]=\"configuration.expanded ?? false\">\n </ng-doc-playground-demo>\n }\n @if (pipeName) {\n <ng-doc-playground-demo\n [id]=\"id\"\n [pipeName]=\"pipeName\"\n [properties]=\"properties\"\n [configuration]=\"configuration\"\n [recreateDemo]=\"recreateDemo\"\n [form]=\"formGroup\">\n </ng-doc-playground-demo>\n }\n </ng-doc-playground-properties>\n}\n", styles: [":host{display:block;margin:var(--ng-doc-playground-margin)}\n"], dependencies: [{ kind: "component", type: NgDocPlaygroundPropertiesComponent, selector: "ng-doc-playground-properties", inputs: ["form", "properties", "ignoreInputs", "dynamicContent", "defaultValues", "hideSidePanel", "recreateDemo", "showResetButton"], outputs: ["recreateDemoChange", "resetForm"] }, { kind: "component", type: NgDocPlaygroundDemoComponent, selector: "ng-doc-playground-demo", inputs: ["id", "pipeName", "selector", "configuration", "properties", "recreateDemo", "form", "expanded"] }, { kind: "pipe", type: NgDocAsArrayPipe, name: "asArray" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
543
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocPlaygroundComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
544
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.2.1", type: NgDocPlaygroundComponent, isStandalone: true, selector: "ng-doc-playground", inputs: { id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: true, transformFunction: null }, pipeName: { classPropertyName: "pipeName", publicName: "pipeName", isSignal: true, isRequired: false, transformFunction: null }, selectors: { classPropertyName: "selectors", publicName: "selectors", isSignal: true, isRequired: false, transformFunction: null }, properties: { classPropertyName: "properties", publicName: "properties", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (configuration(); as configuration) {\n <ng-doc-playground-properties\n [form]=\"formGroup()!\"\n [properties]=\"properties()\"\n [ignoreInputs]=\"configuration.hiddenInputs\"\n [dynamicContent]=\"configuration.content\"\n [hideSidePanel]=\"configuration.hideSidePanel ?? false\"\n [inspectorPosition]=\"configuration.inspectorPosition ?? 'right'\"\n [defaultValues]=\"defaultValues()\"\n [showResetButton]=\"!isDefaultState()\"\n [(recreateDemo)]=\"recreateDemo\"\n (resetForm)=\"resetForm()\">\n @for (selector of configuration.selectors ?? selectors() | asArray; track selector) {\n <ng-doc-playground-demo\n [id]=\"id()\"\n [selector]=\"selector\"\n [properties]=\"properties()\"\n [configuration]=\"configuration\"\n [recreateDemo]=\"recreateDemo()\"\n [form]=\"formGroup()\"\n [expanded]=\"configuration.expanded ?? false\">\n </ng-doc-playground-demo>\n }\n @if (pipeName()) {\n <ng-doc-playground-demo\n [id]=\"id()\"\n [pipeName]=\"pipeName()\"\n [properties]=\"properties()\"\n [configuration]=\"configuration\"\n [recreateDemo]=\"recreateDemo()\"\n [form]=\"formGroup()\">\n </ng-doc-playground-demo>\n }\n </ng-doc-playground-properties>\n}\n", styles: [":host{display:block;margin:var(--ng-doc-playground-margin)}ng-doc-playground-demo:last-child{flex:1 1 auto}ng-doc-playground-demo+ng-doc-playground-demo{border-top:1px solid color-mix(in srgb,var(--ng-doc-border-color) 70%,transparent)}\n"], dependencies: [{ kind: "component", type: NgDocPlaygroundPropertiesComponent, selector: "ng-doc-playground-properties", inputs: ["form", "properties", "ignoreInputs", "dynamicContent", "defaultValues", "hideSidePanel", "inspectorPosition", "recreateDemo", "showResetButton"], outputs: ["recreateDemoChange", "resetForm"] }, { kind: "component", type: NgDocPlaygroundDemoComponent, selector: "ng-doc-playground-demo", inputs: ["id", "pipeName", "selector", "configuration", "properties", "recreateDemo", "form", "expanded"] }, { kind: "pipe", type: NgDocAsArrayPipe, name: "asArray" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
405
545
  }
406
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocPlaygroundComponent, decorators: [{
546
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocPlaygroundComponent, decorators: [{
407
547
  type: Component,
408
- args: [{ selector: 'ng-doc-playground', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgDocPlaygroundPropertiesComponent, NgDocPlaygroundDemoComponent, NgDocAsArrayPipe], template: "@if (configuration) {\n <ng-doc-playground-properties\n [form]=\"formGroup\"\n [properties]=\"properties\"\n [ignoreInputs]=\"configuration.hiddenInputs\"\n [dynamicContent]=\"configuration.content\"\n [hideSidePanel]=\"configuration.hideSidePanel ?? false\"\n [defaultValues]=\"defaultValues\"\n [showResetButton]=\"!isDefaultState()\"\n [(recreateDemo)]=\"recreateDemo\"\n (resetForm)=\"resetForm()\">\n @for (selector of configuration.selectors ?? selectors | asArray; track selector) {\n <ng-doc-playground-demo\n [id]=\"id\"\n [selector]=\"selector\"\n [properties]=\"properties\"\n [configuration]=\"configuration\"\n [recreateDemo]=\"recreateDemo\"\n [form]=\"formGroup\"\n [expanded]=\"configuration.expanded ?? false\">\n </ng-doc-playground-demo>\n }\n @if (pipeName) {\n <ng-doc-playground-demo\n [id]=\"id\"\n [pipeName]=\"pipeName\"\n [properties]=\"properties\"\n [configuration]=\"configuration\"\n [recreateDemo]=\"recreateDemo\"\n [form]=\"formGroup\">\n </ng-doc-playground-demo>\n }\n </ng-doc-playground-properties>\n}\n", styles: [":host{display:block;margin:var(--ng-doc-playground-margin)}\n"] }]
409
- }], ctorParameters: () => [], propDecorators: { id: [{
410
- type: Input,
411
- args: [{ required: true }]
412
- }], pipeName: [{
413
- type: Input
414
- }], selectors: [{
415
- type: Input
416
- }], properties: [{
417
- type: Input
418
- }], options: [{
419
- type: Input
420
- }] } });
548
+ args: [{ selector: 'ng-doc-playground', changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgDocPlaygroundPropertiesComponent, NgDocPlaygroundDemoComponent, NgDocAsArrayPipe], template: "@if (configuration(); as configuration) {\n <ng-doc-playground-properties\n [form]=\"formGroup()!\"\n [properties]=\"properties()\"\n [ignoreInputs]=\"configuration.hiddenInputs\"\n [dynamicContent]=\"configuration.content\"\n [hideSidePanel]=\"configuration.hideSidePanel ?? false\"\n [inspectorPosition]=\"configuration.inspectorPosition ?? 'right'\"\n [defaultValues]=\"defaultValues()\"\n [showResetButton]=\"!isDefaultState()\"\n [(recreateDemo)]=\"recreateDemo\"\n (resetForm)=\"resetForm()\">\n @for (selector of configuration.selectors ?? selectors() | asArray; track selector) {\n <ng-doc-playground-demo\n [id]=\"id()\"\n [selector]=\"selector\"\n [properties]=\"properties()\"\n [configuration]=\"configuration\"\n [recreateDemo]=\"recreateDemo()\"\n [form]=\"formGroup()\"\n [expanded]=\"configuration.expanded ?? false\">\n </ng-doc-playground-demo>\n }\n @if (pipeName()) {\n <ng-doc-playground-demo\n [id]=\"id()\"\n [pipeName]=\"pipeName()\"\n [properties]=\"properties()\"\n [configuration]=\"configuration\"\n [recreateDemo]=\"recreateDemo()\"\n [form]=\"formGroup()\">\n </ng-doc-playground-demo>\n }\n </ng-doc-playground-properties>\n}\n", styles: [":host{display:block;margin:var(--ng-doc-playground-margin)}ng-doc-playground-demo:last-child{flex:1 1 auto}ng-doc-playground-demo+ng-doc-playground-demo{border-top:1px solid color-mix(in srgb,var(--ng-doc-border-color) 70%,transparent)}\n"] }]
549
+ }], propDecorators: { id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: true }] }], pipeName: [{ type: i0.Input, args: [{ isSignal: true, alias: "pipeName", required: false }] }], selectors: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectors", required: false }] }], properties: [{ type: i0.Input, args: [{ isSignal: true, alias: "properties", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }] } });
421
550
 
422
551
  /**
423
- * Base class for playgrounds components.
552
+ * Base class of the playground classes the builder generates, one per playground and selector.
553
+ * A generated class renders the playground template with the target's inputs bound to
554
+ * `properties()`, and queries the target with `viewChild()`.
424
555
  */
425
556
  class NgDocBasePlayground {
426
557
  static { this.selector = 'unknown'; }
427
- constructor(playgroundInstance) {
428
- this.playgroundInstance = playgroundInstance;
429
- this.properties = {};
430
- this.actionData = {};
431
- this.content = {};
558
+ constructor() {
559
+ /** Values of the target's inputs, by property name. */
560
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
561
+ this.properties = input({}, /* @ts-ignore */
562
+ ...(ngDevMode ? [{ debugName: "properties" }] : /* istanbul ignore next */ []));
563
+ /** The `data` given to the playground action; it extends the configuration's `data`. */
564
+ this.actionData = input({}, /* @ts-ignore */
565
+ ...(ngDevMode ? [{ debugName: "actionData" }] : /* istanbul ignore next */ []));
566
+ /** Whether each content slot is shown, by slot name. */
567
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
568
+ this.content = input({}, /* @ts-ignore */
569
+ ...(ngDevMode ? [{ debugName: "content" }] : /* istanbul ignore next */ []));
570
+ /** The default values of the target's inputs, read before the template binds them. */
432
571
  this.defaultValues = {};
433
572
  this.reattached = new Subject();
434
573
  this.playgroundContainer = inject(NgDocPlaygroundComponent);
435
574
  this.changeDetectorRef = inject(ChangeDetectorRef);
575
+ // The view is not checked until the default values of the target are read, so the
576
+ // template's bindings cannot overwrite them first.
436
577
  this.changeDetectorRef.detach();
437
578
  }
579
+ /** Emits once, after the view is attached again and checks the template's bindings. */
438
580
  get onReattached() {
439
581
  return this.reattached.pipe(take(1));
440
582
  }
583
+ /** The playground `data`: the configuration's data extended by the action's data. */
584
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
585
+ get data() {
586
+ return Object.assign({}, this.configData, this.actionData());
587
+ }
441
588
  ngOnInit() {
442
- /*
443
- * Extract default values from playground properties. We do this in `ngOnInit` because in this case
444
- * input values provided from the template are not initialized yet, and we can read default values instead.
445
- */
446
- if (this.playground) {
447
- this.defaultValues = Object.keys(this.playground).reduce((values, key) => {
448
- if (this.playground) {
449
- try {
450
- values[key] =
451
- // @ts-expect-error we do not know the type of the playground
452
- typeof this.playground[key] === 'function'
453
- ? // @ts-expect-error we do not know the type of the playground
454
- this.playground[key]()
455
- : // @ts-expect-error we do not know the type of the playground
456
- this.playground[key];
457
- }
458
- catch (e) {
459
- // we do catch here because some of the playground properties can be getters and throw an error
460
- }
589
+ // The target is created with the view, but the view has not been checked yet, so its inputs
590
+ // still hold their defaults. Signal inputs are read by calling them.
591
+ const playground = this.playground();
592
+ if (playground) {
593
+ this.defaultValues = Object.keys(playground).reduce((values, key) => {
594
+ try {
595
+ const value = playground[key];
596
+ values[key] = typeof value === 'function' ? value.call(playground) : value;
597
+ }
598
+ catch {
599
+ // Some properties are getters or functions that throw without their context.
461
600
  }
462
601
  return values;
463
602
  }, {});
464
603
  }
465
- else if (this.playgroundInstance) {
466
- const defaults = extractFunctionDefaults(this.playgroundInstance.prototype.transform);
467
- this.defaultValues = Object.keys(this.playgroundContainer.properties ?? {}).reduce((def, key, i) => {
468
- // we do +1 because the first argument is the `value` of the transform function
469
- def[key] = defaults[i + 1];
470
- return def;
604
+ else if (this.target) {
605
+ // A pipe: the defaults of its `transform` parameters after the value itself.
606
+ const defaults = extractFunctionDefaults(this.target.prototype.transform);
607
+ this.defaultValues = Object.keys(this.playgroundContainer.properties() ?? {}).reduce((values, key, i) => {
608
+ values[key] = defaults[i + 1];
609
+ return values;
471
610
  }, {});
472
611
  }
473
612
  else {
474
613
  throw new Error('Playground is not defined or initialized');
475
614
  }
476
- if (!this.playgroundContainer.defaultValues) {
477
- this.playgroundContainer.defaultValues = this.defaultValues;
615
+ if (!this.playgroundContainer.defaultValues()) {
616
+ this.playgroundContainer.defaultValues.set(this.defaultValues);
478
617
  }
479
- /*
480
- This is a hack just to wait for the playground container to be initialized and only then
481
- attach the change detector to have correct inputs values.
482
- */
618
+ // The demo sets the playground's inputs right after it creates this view; the view is
619
+ // attached again once they are set, so its first check renders the playground's values.
483
620
  Promise.resolve().then(() => {
484
621
  this.changeDetectorRef.reattach();
622
+ this.changeDetectorRef.markForCheck();
485
623
  this.reattached.next();
486
624
  });
487
625
  }
488
- get data() {
489
- return Object.assign({}, this.configData, this.actionData);
490
- }
491
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocBasePlayground, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive }); }
492
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: NgDocBasePlayground, isStandalone: true, inputs: { properties: "properties", actionData: "actionData", content: "content" }, ngImport: i0 }); }
626
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocBasePlayground, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
627
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.2.1", type: NgDocBasePlayground, isStandalone: true, inputs: { properties: { classPropertyName: "properties", publicName: "properties", isSignal: true, isRequired: false, transformFunction: null }, actionData: { classPropertyName: "actionData", publicName: "actionData", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
493
628
  }
494
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: NgDocBasePlayground, decorators: [{
629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.2.1", ngImport: i0, type: NgDocBasePlayground, decorators: [{
495
630
  type: Directive
496
- }], ctorParameters: () => [{ type: undefined }], propDecorators: { properties: [{
497
- type: Input
498
- }], actionData: [{
499
- type: Input
500
- }], content: [{
501
- type: Input
502
- }] } });
631
+ }], ctorParameters: () => [], propDecorators: { properties: [{ type: i0.Input, args: [{ isSignal: true, alias: "properties", required: false }] }], actionData: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionData", required: false }] }], content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }] } });
503
632
 
504
633
  /**
505
634
  * Generated bundle index. Do not edit.