@ng-prism/core 22.5.0 → 22.6.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 (118) hide show
  1. package/README.md +2 -0
  2. package/dist/app/canvas/prism-canvas-toolbar.component.d.ts +22 -1
  3. package/dist/app/canvas/prism-canvas-toolbar.component.d.ts.map +1 -1
  4. package/dist/app/canvas/prism-canvas-toolbar.component.js +111 -38
  5. package/dist/app/canvas/state-chips.d.ts +50 -0
  6. package/dist/app/canvas/state-chips.d.ts.map +1 -0
  7. package/dist/app/canvas/state-chips.js +40 -0
  8. package/dist/app/component-head/prism-component-head.component.d.ts +1 -0
  9. package/dist/app/component-head/prism-component-head.component.d.ts.map +1 -1
  10. package/dist/app/component-head/prism-component-head.component.js +11 -6
  11. package/dist/app/component-head/prism-head-info.component.d.ts +2 -1
  12. package/dist/app/component-head/prism-head-info.component.d.ts.map +1 -1
  13. package/dist/app/component-head/prism-head-info.component.js +30 -7
  14. package/dist/app/directives/resize-value.d.ts +1 -1
  15. package/dist/app/directives/resize-value.js +1 -1
  16. package/dist/app/discovery-manifest.d.ts +2 -2
  17. package/dist/app/discovery-manifest.d.ts.map +1 -1
  18. package/dist/app/discovery-manifest.js +15 -3
  19. package/dist/app/overview/open-in-playground.d.ts +11 -0
  20. package/dist/app/overview/open-in-playground.d.ts.map +1 -0
  21. package/dist/app/overview/open-in-playground.js +12 -0
  22. package/dist/app/overview/overview-matrix.d.ts +79 -0
  23. package/dist/app/overview/overview-matrix.d.ts.map +1 -0
  24. package/dist/app/overview/overview-matrix.js +98 -0
  25. package/dist/app/overview/overview-visibility.d.ts +10 -2
  26. package/dist/app/overview/overview-visibility.d.ts.map +1 -1
  27. package/dist/app/overview/overview-visibility.js +14 -3
  28. package/dist/app/overview/prism-overview-cell.component.d.ts +23 -2
  29. package/dist/app/overview/prism-overview-cell.component.d.ts.map +1 -1
  30. package/dist/app/overview/prism-overview-cell.component.js +133 -27
  31. package/dist/app/overview/prism-overview-matrix.component.d.ts +29 -0
  32. package/dist/app/overview/prism-overview-matrix.component.d.ts.map +1 -0
  33. package/dist/app/overview/prism-overview-matrix.component.js +164 -0
  34. package/dist/app/overview/prism-overview.component.d.ts +4 -0
  35. package/dist/app/overview/prism-overview.component.d.ts.map +1 -1
  36. package/dist/app/overview/prism-overview.component.js +37 -14
  37. package/dist/app/panels/a11y/a11y-core.plugin.d.ts.map +1 -1
  38. package/dist/app/panels/a11y/a11y-core.plugin.js +0 -1
  39. package/dist/app/panels/controls/controls-plugin.d.ts.map +1 -1
  40. package/dist/app/panels/controls/controls-plugin.js +1 -2
  41. package/dist/app/panels/events/events-plugin.d.ts.map +1 -1
  42. package/dist/app/panels/events/events-plugin.js +1 -2
  43. package/dist/app/pseudo/pseudo-state-marker.d.ts +32 -10
  44. package/dist/app/pseudo/pseudo-state-marker.d.ts.map +1 -1
  45. package/dist/app/pseudo/pseudo-state-marker.js +83 -67
  46. package/dist/app/pseudo/pseudo-targets.d.ts +11 -0
  47. package/dist/app/pseudo/pseudo-targets.d.ts.map +1 -0
  48. package/dist/app/pseudo/pseudo-targets.js +46 -0
  49. package/dist/app/pseudo/state-fit.d.ts +28 -0
  50. package/dist/app/pseudo/state-fit.d.ts.map +1 -0
  51. package/dist/app/pseudo/state-fit.js +97 -0
  52. package/dist/app/renderer/input-bindings.d.ts +16 -0
  53. package/dist/app/renderer/input-bindings.d.ts.map +1 -0
  54. package/dist/app/renderer/input-bindings.js +21 -0
  55. package/dist/app/renderer/known-inputs.d.ts +8 -1
  56. package/dist/app/renderer/known-inputs.d.ts.map +1 -1
  57. package/dist/app/renderer/known-inputs.js +13 -5
  58. package/dist/app/renderer/prism-renderer.component.d.ts +10 -3
  59. package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
  60. package/dist/app/renderer/prism-renderer.component.js +51 -29
  61. package/dist/app/renderer/projectable-content.d.ts +8 -1
  62. package/dist/app/renderer/projectable-content.d.ts.map +1 -1
  63. package/dist/app/renderer/projectable-content.js +40 -29
  64. package/dist/app/renderer/rendered-key.d.ts +20 -6
  65. package/dist/app/renderer/rendered-key.d.ts.map +1 -1
  66. package/dist/app/renderer/rendered-key.js +25 -8
  67. package/dist/app/renderer/snippet-generator.js +7 -5
  68. package/dist/app/services/prism-pseudo-state.service.d.ts +59 -14
  69. package/dist/app/services/prism-pseudo-state.service.d.ts.map +1 -1
  70. package/dist/app/services/prism-pseudo-state.service.js +86 -29
  71. package/dist/app/services/prism-renderer.service.d.ts +17 -0
  72. package/dist/app/services/prism-renderer.service.d.ts.map +1 -1
  73. package/dist/app/services/prism-renderer.service.js +37 -2
  74. package/dist/app/services/prism-url-state.service.d.ts.map +1 -1
  75. package/dist/app/services/prism-url-state.service.js +39 -8
  76. package/dist/app/theme/prism-default-theme.d.ts.map +1 -1
  77. package/dist/app/theme/prism-default-theme.js +6 -5
  78. package/dist/builder/config-loader/deprecated-options.d.ts +7 -0
  79. package/dist/builder/config-loader/deprecated-options.d.ts.map +1 -0
  80. package/dist/builder/config-loader/deprecated-options.js +23 -0
  81. package/dist/builder/manifest/runtime-manifest.generator.d.ts.map +1 -1
  82. package/dist/builder/manifest/runtime-manifest.generator.js +3 -2
  83. package/dist/builder/scanner/component.scanner.d.ts.map +1 -1
  84. package/dist/builder/scanner/component.scanner.js +20 -1
  85. package/dist/builder/scanner/entry-point-discovery.d.ts +5 -0
  86. package/dist/builder/scanner/entry-point-discovery.d.ts.map +1 -1
  87. package/dist/builder/scanner/entry-point-discovery.js +14 -0
  88. package/dist/builder/scanner/entry-point.scanner.d.ts +3 -1
  89. package/dist/builder/scanner/entry-point.scanner.d.ts.map +1 -1
  90. package/dist/builder/scanner/entry-point.scanner.js +7 -6
  91. package/dist/builder/scanner/input.extractor.d.ts.map +1 -1
  92. package/dist/builder/scanner/input.extractor.js +22 -3
  93. package/dist/builder/scanner/pseudo-states.d.ts +1 -0
  94. package/dist/builder/scanner/pseudo-states.d.ts.map +1 -1
  95. package/dist/builder/scanner/pseudo-states.js +1 -1
  96. package/dist/builder/scanner/scanner.d.ts +2 -2
  97. package/dist/builder/scanner/scanner.d.ts.map +1 -1
  98. package/dist/builder/scanner/scanner.js +16 -6
  99. package/dist/builder/scanner/showcase-diagnostics.js +9 -9
  100. package/dist/builder/scanner/showcase-states.d.ts +17 -0
  101. package/dist/builder/scanner/showcase-states.d.ts.map +1 -0
  102. package/dist/builder/scanner/showcase-states.js +58 -0
  103. package/dist/builder/scanner/source-file-cache.d.ts +26 -0
  104. package/dist/builder/scanner/source-file-cache.d.ts.map +1 -0
  105. package/dist/builder/scanner/source-file-cache.js +60 -0
  106. package/dist/builder/shared/prism-pipeline.d.ts.map +1 -1
  107. package/dist/builder/shared/prism-pipeline.js +28 -13
  108. package/dist/decorator/index.d.ts +1 -1
  109. package/dist/decorator/index.d.ts.map +1 -1
  110. package/dist/decorator/showcase.types.d.ts +23 -0
  111. package/dist/decorator/showcase.types.d.ts.map +1 -1
  112. package/dist/plugin/index.d.ts +1 -1
  113. package/dist/plugin/index.d.ts.map +1 -1
  114. package/dist/plugin/plugin.types.d.ts +44 -5
  115. package/dist/plugin/plugin.types.d.ts.map +1 -1
  116. package/package.json +1 -1
  117. package/src/builder/build/schema.json +6 -6
  118. package/src/builder/serve/schema.json +6 -6
@@ -1 +1 @@
1
- {"version":3,"file":"prism-renderer.component.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/prism-renderer.component.ts"],"names":[],"mappings":"AACA,OAAO,EASH,QAAQ,EAER,KAAK,IAAI,EAIZ,MAAM,eAAe,CAAC;AAYvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAG3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAIjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;;AAOhF,qBASa,sBAAsB;IAC/B,SAAS,CAAC,QAAQ,CAAC,iBAAiB,yBAAkC;IACtE,SAAS,CAAC,QAAQ,CAAC,eAAe,uBAAgC;IAClE,SAAS,CAAC,QAAQ,CAAC,aAAa,qBAA8B;IAC9D,SAAS,CAAC,QAAQ,CAAC,OAAO,sBAA+B;IACzD,SAAS,CAAC,QAAQ,CAAC,SAAS,wBAAiC;IAC7D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmC;IAE/D;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,gDAAuF;IAEvH,SAAS,CAAC,QAAQ,CAAC,YAAY,OAAgB;IAC/C,SAAS,CAAC,QAAQ,CAAC,YAAY,QAAgB;IAE/C;;;;;;;OAOG;IACH,SAAS,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmC;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAE3B;IAEH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAC5D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;IAEhE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAEpB;IACH,OAAO,CAAC,YAAY,CAAsC;IAC1D,OAAO,CAAC,mBAAmB,CAAsC;IACjE,OAAO,CAAC,oBAAoB,CAA0D;IACtF,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoC;IACjE,QAAQ,CAAC,aAAa,+DAAsC;IAC5D;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,WAAW,gDAQ5B;IACF;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,gDAI5B;IACH,4GAA4G;IAC5G,SAAS,CAAC,QAAQ,CAAC,YAAY,4FAO5B;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa;;MAA6C;IAC7E,SAAS,CAAC,QAAQ,CAAC,eAAe,yFAI/B;;IA2FH,OAAO,CAAC,eAAe;IA8EvB,OAAO,CAAC,OAAO;yCA1QN,sBAAsB;2CAAtB,sBAAsB;CAyRlC"}
1
+ {"version":3,"file":"prism-renderer.component.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/prism-renderer.component.ts"],"names":[],"mappings":"AACA,OAAO,EASH,QAAQ,EAGR,KAAK,IAAI,EAIZ,MAAM,eAAe,CAAC;AAYvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAG3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAIjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;;AAOhF,qBASa,sBAAsB;IAC/B,SAAS,CAAC,QAAQ,CAAC,iBAAiB,yBAAkC;IACtE,SAAS,CAAC,QAAQ,CAAC,eAAe,uBAAgC;IAClE,SAAS,CAAC,QAAQ,CAAC,aAAa,qBAA8B;IAC9D,SAAS,CAAC,QAAQ,CAAC,OAAO,sBAA+B;IACzD,SAAS,CAAC,QAAQ,CAAC,SAAS,wBAAiC;IAC7D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmC;IAE/D;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa,gDAAuF;IAEvH,SAAS,CAAC,QAAQ,CAAC,YAAY,OAAgB;IAC/C,SAAS,CAAC,QAAQ,CAAC,YAAY,QAAgB;IAE/C;;;;;;;OAOG;IACH,SAAS,CAAC,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmC;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAE3B;IAEH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAC5D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;IAEhE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAEpB;IACH,OAAO,CAAC,YAAY,CAAsC;IAC1D,OAAO,CAAC,mBAAmB,CAAsC;IACjE,OAAO,CAAC,oBAAoB,CAA0D;IACtF,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoC;IACjE,QAAQ,CAAC,aAAa,+DAAsC;IAC5D;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,WAAW,gDAY3B;IACH;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,gDAE/B;IACF;;;;OAIG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,gDAI5B;IACH,4GAA4G;IAC5G,SAAS,CAAC,QAAQ,CAAC,YAAY,4FAO5B;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa;;MAA6C;IAC7E,SAAS,CAAC,QAAQ,CAAC,eAAe,yFAI/B;;IAmGH,OAAO,CAAC,eAAe;IAoFvB,OAAO,CAAC,OAAO;yCArSN,sBAAsB;2CAAtB,sBAAsB;CAoTlC"}
@@ -1,6 +1,5 @@
1
1
  import { NgComponentOutlet } from '@angular/common';
2
- import { ChangeDetectionStrategy, Component, computed, DestroyRef, effect, ElementRef, inject, Injector, signal, untracked, viewChild, ViewContainerRef } from '@angular/core';
3
- import { PSEUDO_STATES } from '../../shared/pseudo-state.type.js';
2
+ import { ChangeDetectionStrategy, Component, computed, DestroyRef, effect, ElementRef, inject, Injector, reflectComponentType, signal, untracked, viewChild, ViewContainerRef } from '@angular/core';
4
3
  import { VIEWPORT_MAX, VIEWPORT_MIN } from '../../shared/viewport.type.js';
5
4
  import { CANVAS_BG_STYLES } from '../canvas/canvas-bg.styles.js';
6
5
  import { PrismCanvasBgPillComponent } from '../canvas/prism-canvas-bg-pill.component.js';
@@ -9,6 +8,7 @@ import { PrismCanvasRulersComponent } from '../canvas/prism-canvas-rulers.compon
9
8
  import { snapViewportWidth } from '../canvas/viewport-snap.js';
10
9
  import { PrismResizerDirective } from '../directives/prism-resizer.directive.js';
11
10
  import { BUILTIN_PANELS } from '../panels/builtin-panels.js';
11
+ import { appliedStateNames } from '../pseudo/pseudo-state-marker.js';
12
12
  import { PrismCanvasService } from '../services/prism-canvas.service.js';
13
13
  import { PrismCaptureService } from '../services/prism-capture.service.js';
14
14
  import { PrismEventLogService } from '../services/prism-event-log.service.js';
@@ -20,10 +20,10 @@ import { PrismPseudoStateService } from '../services/prism-pseudo-state.service.
20
20
  import { PrismRendererService } from '../services/prism-renderer.service.js';
21
21
  import { PrismVariantBgService } from '../services/prism-variant-bg.service.js';
22
22
  import { PRISM_RENDERER_HOOKS } from '../tokens/prism-tokens.js';
23
- import { buildKnownInputs } from './known-inputs.js';
23
+ import { buildInputBindings } from './input-bindings.js';
24
24
  import { resolveOverlay } from './overlay-resolver.js';
25
25
  import { parseContentToNodes } from './projectable-content.js';
26
- import { resolveRenderedKey } from './rendered-key.js';
26
+ import { resolveExcludedReason, resolveRenderedKey } from './rendered-key.js';
27
27
  import * as i0 from "@angular/core";
28
28
  const _c0 = ["outlet"];
29
29
  function PrismRendererComponent_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
@@ -134,24 +134,37 @@ export class PrismRendererComponent {
134
134
  activeOverlay = signal(null, /* @ts-ignore */
135
135
  ...(ngDevMode ? [{ debugName: "activeOverlay" }] : /* istanbul ignore next */ []));
136
136
  /**
137
- * Identifier of the currently rendered component/variant (for audit tooling / e2e).
138
- * Held back until the variant's forced pseudo states are in place, see {@link resolveRenderedKey}.
137
+ * Identifier of the currently rendered component/variant, and of the active state.
138
+ * Held back until the forced pseudo states and the state are in place, see {@link resolveRenderedKey}.
139
139
  */
140
- renderedKey = computed(() => resolveRenderedKey({
141
- component: this.navigationService.activeComponent(),
142
- variantIndex: this.rendererService.activeVariantIndex(),
143
- element: this.rendererService.renderedElement(),
144
- pseudo: this.pseudoState.effective(),
145
- pseudoApplied: this.pseudoMarker.applied()
146
- }), /* @ts-ignore */
140
+ renderedKey = computed(() => {
141
+ const { base, state } = this.pseudoState.layers();
142
+ return resolveRenderedKey({
143
+ component: this.navigationService.activeComponent(),
144
+ variantIndex: this.rendererService.activeVariantIndex(),
145
+ stateIndex: this.rendererService.activeStateIndex(),
146
+ element: this.rendererService.renderedElement(),
147
+ pseudo: base,
148
+ state,
149
+ pseudoApplied: this.pseudoMarker.applied()
150
+ });
151
+ }, /* @ts-ignore */
147
152
  ...(ngDevMode ? [{ debugName: "renderedKey" }] : /* istanbul ignore next */ []));
153
+ /**
154
+ * Why the active state is not part of what the canvas shows, `null` when it is
155
+ * or none is active. A runner reads it next to `data-prism-rendered` and
156
+ * reports the cell as excluded rather than taking a picture of it.
157
+ */
158
+ excludedReason = computed(() => resolveExcludedReason(this.rendererService.activeStateIndex(), this.pseudoState.stateExclusion(), this.pseudoMarker.applied()), /* @ts-ignore */
159
+ ...(ngDevMode ? [{ debugName: "excludedReason" }] : /* istanbul ignore next */ []));
148
160
  /**
149
161
  * The states forced on the canvas, space-separated, `null` for none. Capture
150
- * mode keys the room it leaves for focus rings on it.
162
+ * mode keys the room it leaves for focus rings on it. The active state's
163
+ * pseudo-classes count only while it fits.
151
164
  */
152
165
  forcedStates = computed(() => {
153
- const states = this.pseudoMarker.applied()?.states;
154
- return states ? PSEUDO_STATES.filter((state) => states[state] !== undefined).join(' ') : null;
166
+ const names = appliedStateNames(this.pseudoMarker.applied());
167
+ return names.length > 0 ? names.join(' ') : null;
155
168
  }, /* @ts-ignore */
156
169
  ...(ngDevMode ? [{ debugName: "forcedStates" }] : /* istanbul ignore next */ []));
157
170
  /** Resolved canvas layout for the active variant. Variant overrides component config; defaults to 'fit'. */
@@ -198,13 +211,14 @@ export class PrismRendererComponent {
198
211
  return;
199
212
  }
200
213
  performance.mark('prism:rerender:start');
201
- const knownInputs = buildKnownInputs(comp);
214
+ const inputBindings = buildInputBindings(comp);
202
215
  for (const [key, value] of Object.entries(inputs)) {
203
- if (!knownInputs.has(key)) {
216
+ const binding = inputBindings.get(key);
217
+ if (binding === undefined) {
204
218
  console.warn(`[ng-prism] Unknown input "${key}" on <${comp.meta.componentMeta.selector}>, skipping. Remove it from @Showcase variants.`);
205
219
  continue;
206
220
  }
207
- ref.setInput(key, value);
221
+ ref.setInput(binding, value);
208
222
  }
209
223
  ref.changeDetectorRef.detectChanges();
210
224
  performance.mark('prism:rerender:end');
@@ -236,8 +250,13 @@ export class PrismRendererComponent {
236
250
  // `renderedKey` waits for what this applies.
237
251
  effect(() => {
238
252
  const element = this.rendererService.renderedElement();
239
- const states = this.pseudoState.effective();
240
- untracked(() => this.pseudoMarker.apply(element, states));
253
+ const { base, state } = this.pseudoState.layers();
254
+ untracked(() => this.pseudoMarker.apply(element, base, state));
255
+ });
256
+ // What the layers and the toggles of the tools menu depend on. Written from the marker alone and never
257
+ // read back here: `null` whenever no state layer is applied, so the fit of the previous state cannot linger.
258
+ effect(() => {
259
+ this.pseudoState.canvasFit.set(this.pseudoMarker.applied()?.fit ?? null);
241
260
  });
242
261
  this.destroyRef.onDestroy(() => {
243
262
  this.cleanup();
@@ -274,7 +293,9 @@ export class PrismRendererComponent {
274
293
  });
275
294
  const content = this.rendererService.activeContent();
276
295
  this.lastProjectedContent = content;
277
- const projectableNodes = content ? parseContentToNodes(content) : undefined;
296
+ const projectableNodes = content
297
+ ? parseContentToNodes(content, reflectComponentType(comp.type)?.ngContentSelectors ?? [], (key) => console.warn(`[ng-prism] Variant content "${key}" matches no <ng-content> of ${comp.meta.className}, skipping it.`))
298
+ : undefined;
278
299
  this.componentRef = this.outlet().createComponent(comp.type, {
279
300
  injector,
280
301
  projectableNodes
@@ -286,13 +307,14 @@ export class PrismRendererComponent {
286
307
  this.outputSubscriptions.push(sub);
287
308
  }
288
309
  }
289
- const knownInputs = buildKnownInputs(comp);
310
+ const inputBindings = buildInputBindings(comp);
290
311
  for (const [key, value] of Object.entries(this.rendererService.inputValues())) {
291
- if (!knownInputs.has(key)) {
312
+ const binding = inputBindings.get(key);
313
+ if (binding === undefined) {
292
314
  console.warn(`[ng-prism] Unknown input "${key}" on <${selector}>, skipping. Remove it from @Showcase variants.`);
293
315
  continue;
294
316
  }
295
- this.componentRef.setInput(key, value);
317
+ this.componentRef.setInput(binding, value);
296
318
  }
297
319
  this.componentRef.changeDetectorRef.detectChanges();
298
320
  performance.mark('prism:render:end', detail);
@@ -319,7 +341,7 @@ export class PrismRendererComponent {
319
341
  i0.ɵɵviewQuerySignal(ctx.outlet, _c0, 5, ViewContainerRef);
320
342
  } if (rf & 2) {
321
343
  i0.ɵɵqueryAdvance();
322
- } }, decls: 6, vars: 15, consts: [["outlet", ""], [1, "prism-canvas-stage"], [1, "demo-wrap"], [1, "stage-crosshair"], [1, "vp-dim"], [1, "vp-dim__rule"], [1, "vp-dim__v"], ["prismResizer", "", "axis", "x", "aria-label", "Viewport width, left edge", 1, "vp-grip", 3, "valueChange", "scale", "min", "max", "value"], [1, "vp-grip__bar"], ["prismResizer", "", "axis", "x", "aria-label", "Viewport width, right edge", 1, "vp-grip", "vp-grip--end", 3, "valueChange", "scale", "min", "max", "value"], [4, "ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector"]], template: function PrismRendererComponent_Template(rf, ctx) { if (rf & 1) {
344
+ } }, decls: 6, vars: 16, consts: [["outlet", ""], [1, "prism-canvas-stage"], [1, "demo-wrap"], [1, "stage-crosshair"], [1, "vp-dim"], [1, "vp-dim__rule"], [1, "vp-dim__v"], ["prismResizer", "", "axis", "x", "aria-label", "Viewport width, left edge", 1, "vp-grip", 3, "valueChange", "scale", "min", "max", "value"], [1, "vp-grip__bar"], ["prismResizer", "", "axis", "x", "aria-label", "Viewport width, right edge", 1, "vp-grip", "vp-grip--end", 3, "valueChange", "scale", "min", "max", "value"], [4, "ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector"]], template: function PrismRendererComponent_Template(rf, ctx) { if (rf & 1) {
323
345
  i0.ɵɵelementStart(0, "div", 1);
324
346
  i0.ɵɵconditionalCreate(1, PrismRendererComponent_Conditional_1_Template, 5, 4);
325
347
  i0.ɵɵelementStart(2, "div", 2);
@@ -333,15 +355,15 @@ export class PrismRendererComponent {
333
355
  i0.ɵɵconditional(!ctx.capture.active() ? 1 : -1);
334
356
  i0.ɵɵadvance();
335
357
  i0.ɵɵstyleProp("--%NS%zoom", ctx.canvasService.zoom());
336
- i0.ɵɵattribute("data-prism-rendered", ctx.renderedKey())("data-canvas-layout", ctx.canvasLayout())("data-viewport", ctx.viewportWidth() !== null ? "" : null)("data-pseudo", ctx.forcedStates());
358
+ i0.ɵɵattribute("data-prism-rendered", ctx.renderedKey())("data-prism-excluded", ctx.excludedReason())("data-canvas-layout", ctx.canvasLayout())("data-viewport", ctx.viewportWidth() !== null ? "" : null)("data-pseudo", ctx.forcedStates());
337
359
  i0.ɵɵadvance(3);
338
360
  i0.ɵɵconditional(ctx.activeOverlay() ? 5 : -1);
339
361
  } }, dependencies: [PrismCanvasRulersComponent, PrismCanvasBgPillComponent, PrismCanvasMeasureComponent, NgComponentOutlet, PrismResizerDirective], styles: ["[_nghost-%COMP%] {\n display: block;\n flex: 1;\n min-height: 0;\n}\n\n.prism-canvas-stage[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 200px;\n padding: 32px;\n overflow: auto;\n\n \n\n\n\n\n\n\n\n\n\n\n outline: 1px solid var(--%NS%prism-stage-edge);\n outline-offset: -1px;\n background-color: var(--%NS%prism-stage);\n box-shadow:\n 0 1px 3px rgb(0 0 0 / 10%),\n 0 8px 24px -12px rgb(0 0 0 / 18%);\n transition: filter var(--%NS%dur-base);\n\n --%NS%prism-canvas-overlay-top: 12px;\n --%NS%prism-canvas-overlay-inline: 20px;\n}\n\n.prism-canvas-stage[data-rulers][_ngcontent-%COMP%] {\n --%NS%prism-canvas-overlay-top: 28px;\n --%NS%prism-canvas-overlay-inline: 28px;\n}\n\n.stage-crosshair[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n pointer-events: none;\n opacity: 0;\n transition: opacity var(--%NS%dur-base);\n}\n\n.stage-crosshair.visible[_ngcontent-%COMP%] {\n opacity: 1;\n}\n\n.stage-crosshair[_ngcontent-%COMP%]::before, \n.stage-crosshair[_ngcontent-%COMP%]::after {\n position: absolute;\n content: '';\n background: color-mix(in srgb, var(--%NS%prism-primary) 20%, transparent);\n}\n\n.stage-crosshair[_ngcontent-%COMP%]::before {\n top: 50%;\n right: 0;\n left: 0;\n height: 1px;\n}\n\n.stage-crosshair[_ngcontent-%COMP%]::after {\n top: 0;\n bottom: 0;\n left: 50%;\n width: 1px;\n}\n\n.demo-wrap[_ngcontent-%COMP%] {\n position: relative;\n display: inline-block;\n transform: scale(var(--%NS%zoom, 1));\n transition: transform 0.18s;\n}\n\n.demo-wrap[data-canvas-layout='stretch'][_ngcontent-%COMP%] {\n display: block;\n width: 100%;\n max-width: 800px;\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.demo-wrap[data-viewport][_ngcontent-%COMP%] {\n display: grid;\n\n \n\n\n\n\n\n\n\n\n flex: none;\n\n \n\n\n\n\n\n\n justify-items: safe center;\n width: var(--%NS%prism-vp-w);\n max-width: none;\n container-type: inline-size;\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n.vp-grip[_ngcontent-%COMP%] {\n position: absolute;\n top: var(--%NS%prism-canvas-overlay-top, 12px);\n bottom: 0;\n left: calc(50% - var(--%NS%prism-vp-w) * var(--%NS%zoom, 1) / 2 - 4.5px);\n z-index: 4;\n display: grid;\n width: 9px;\n\n \n\n\n\n\n\n pointer-events: none;\n background: transparent;\n}\n\n.vp-grip--end[_ngcontent-%COMP%] {\n right: calc(50% - var(--%NS%prism-vp-w) * var(--%NS%zoom, 1) / 2 - 4.5px);\n left: auto;\n}\n\n\n\n\n\n\n\n\n\n\n\n\n.vp-grip__bar[_ngcontent-%COMP%] {\n z-index: 1;\n box-sizing: content-box;\n grid-area: 1 / 1;\n place-self: center center;\n width: 3px;\n height: 34px;\n padding: 5px 3px;\n pointer-events: auto;\n background: color-mix(in srgb, var(--%NS%prism-measure) 80%, transparent);\n background-clip: content-box;\n border-radius: 2px;\n transition: background var(--%NS%dur-fast);\n}\n\n\n\n\n\n\n.vp-grip[_ngcontent-%COMP%]::after {\n grid-area: 1 / 1;\n place-self: stretch center;\n width: 1px;\n content: '';\n background: color-mix(in srgb, var(--%NS%prism-measure) 75%, transparent);\n transition: background var(--%NS%dur-fast);\n}\n\n.vp-grip[_ngcontent-%COMP%]:hover::after, \n.vp-grip.active[_ngcontent-%COMP%]::after, \n.vp-grip[_ngcontent-%COMP%]:focus-visible::after {\n background: color-mix(in srgb, var(--%NS%prism-measure) 55%, transparent);\n}\n\n.vp-grip[_ngcontent-%COMP%]:hover .vp-grip__bar[_ngcontent-%COMP%], \n.vp-grip.active[_ngcontent-%COMP%] .vp-grip__bar[_ngcontent-%COMP%], \n.vp-grip[_ngcontent-%COMP%]:focus-visible .vp-grip__bar[_ngcontent-%COMP%] {\n background: var(--%NS%prism-measure);\n background-clip: content-box;\n}\n\n\n\n\n.vp-grip[_ngcontent-%COMP%]:focus-visible {\n outline: none;\n}\n\n.vp-grip[_ngcontent-%COMP%]:focus-visible .vp-grip__bar[_ngcontent-%COMP%] {\n outline: 2px solid var(--%NS%prism-primary);\n outline-offset: 1px;\n}\n\n\n\n\n\n\n\n\n\n\n.vp-dim[_ngcontent-%COMP%] {\n position: absolute;\n top: var(--%NS%prism-canvas-overlay-top, 12px);\n left: 50%;\n z-index: 4;\n display: flex;\n gap: var(--%NS%sp-3);\n align-items: center;\n width: calc(var(--%NS%prism-vp-w) * var(--%NS%zoom, 1));\n pointer-events: none;\n transform: translateX(-50%);\n}\n\n\n\n\n.vp-dim__rule[_ngcontent-%COMP%] {\n position: relative;\n flex: 1;\n height: 1px;\n background: color-mix(in srgb, var(--%NS%prism-measure) 70%, transparent);\n}\n\n\n\n\n.vp-dim__rule[_ngcontent-%COMP%]::before {\n position: absolute;\n top: -3px;\n width: 1px;\n height: 7px;\n content: '';\n background: color-mix(in srgb, var(--%NS%prism-measure) 90%, transparent);\n}\n\n.vp-dim__rule[_ngcontent-%COMP%]:first-child::before {\n left: 0;\n}\n\n.vp-dim__rule[_ngcontent-%COMP%]:last-child::before {\n right: 0;\n}\n\n.vp-dim__v[_ngcontent-%COMP%] {\n flex: none;\n font-family: var(--%NS%font-mono);\n font-size: 9px;\n color: var(--%NS%prism-measure);\n white-space: nowrap;\n}", "[data-bg=\"dots\"][_ngcontent-%COMP%] {\n background-image: radial-gradient(circle, var(--%NS%prism-dot) 1px, transparent 1px);\n background-size: 20px 20px;\n }\n\n [data-bg=\"plain\"][_ngcontent-%COMP%] {\n background-image: none;\n }\n\n \n\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n [data-bg=\"light\"][_ngcontent-%COMP%] {\n background-color: var(--%NS%prism-void-light, #f7f5fc);\n background-image: none;\n --%NS%prism-measure: #ca00c1;\n --%NS%prism-measure-plate: var(--%NS%prism-void-light, #f7f5fc);\n }\n [data-bg=\"dark\"][_ngcontent-%COMP%] {\n background-color: var(--%NS%prism-void-dark, #07050f);\n background-image: none;\n --%NS%prism-measure: #f632ff;\n --%NS%prism-measure-plate: var(--%NS%prism-void-dark, #07050f);\n }\n\n \n\n\n [data-bg=\"checker\"][_ngcontent-%COMP%], \n [data-bg=\"transparent\"][_ngcontent-%COMP%] {\n background-image:\n linear-gradient(45deg, var(--%NS%prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--%NS%prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--%NS%prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--%NS%prism-border) 75%);\n background-size: 16px 16px;\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n }"] });
340
362
  }
341
363
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismRendererComponent, [{
342
364
  type: Component,
343
- args: [{ selector: 'prism-renderer', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [PrismCanvasRulersComponent, PrismCanvasBgPillComponent, PrismCanvasMeasureComponent, NgComponentOutlet, PrismResizerDirective], template: "<div\n class=\"prism-canvas-stage\"\n [attr.data-bg]=\"variantBg.effective()\"\n [attr.data-rulers]=\"canvasService.rulers() ? '' : null\"\n [attr.data-viewport]=\"viewportWidth() !== null ? '' : null\"\n [style.--prism-vp-w.px]=\"viewportWidth()\"\n [style.--zoom]=\"canvasService.zoom()\">\n @if (!capture.active()) {\n <div\n class=\"stage-crosshair\"\n [class.visible]=\"canvasService.guides()\"></div>\n <prism-canvas-rulers />\n @if (canvasService.measure()) {\n <prism-canvas-measure />\n }\n <prism-canvas-bg-pill />\n @if (viewportWidth() !== null) {\n <!-- Grips and the dimension line are siblings of .demo-wrap, never\n children of it: .demo-wrap is what plugin-visual-regression\n screenshots, and anything inside it composites into every baseline.\n They sit inside the capture guard for the same reason the rulers and\n the pill do. -->\n <span class=\"vp-dim\">\n <span class=\"vp-dim__rule\"></span>\n <span class=\"vp-dim__v\">{{ viewportWidth() }} px</span>\n <span class=\"vp-dim__rule\"></span>\n </span>\n <div\n class=\"vp-grip\"\n prismResizer\n axis=\"x\"\n [scale]=\"-2 / (canvasService.zoom() || 1)\"\n [min]=\"VIEWPORT_MIN\"\n [max]=\"VIEWPORT_MAX\"\n [value]=\"viewportWidth()!\"\n (valueChange)=\"onViewportResize($event)\"\n aria-label=\"Viewport width, left edge\"\n [attr.aria-valuenow]=\"viewportWidth()\"\n [attr.aria-valuemin]=\"VIEWPORT_MIN\"\n [attr.aria-valuemax]=\"VIEWPORT_MAX\">\n <span class=\"vp-grip__bar\"></span>\n </div>\n <div\n class=\"vp-grip vp-grip--end\"\n prismResizer\n axis=\"x\"\n [scale]=\"2 / (canvasService.zoom() || 1)\"\n [min]=\"VIEWPORT_MIN\"\n [max]=\"VIEWPORT_MAX\"\n [value]=\"viewportWidth()!\"\n (valueChange)=\"onViewportResize($event)\"\n aria-label=\"Viewport width, right edge\"\n [attr.aria-valuenow]=\"viewportWidth()\"\n [attr.aria-valuemin]=\"VIEWPORT_MIN\"\n [attr.aria-valuemax]=\"VIEWPORT_MAX\">\n <span class=\"vp-grip__bar\"></span>\n </div>\n }\n }\n\n <div\n class=\"demo-wrap\"\n [style.--zoom]=\"canvasService.zoom()\"\n [attr.data-prism-rendered]=\"renderedKey()\"\n [attr.data-canvas-layout]=\"canvasLayout()\"\n [attr.data-viewport]=\"viewportWidth() !== null ? '' : null\"\n [attr.data-pseudo]=\"forcedStates()\">\n <ng-container #outlet />\n @if (activeOverlay()) {\n <ng-container *ngComponentOutlet=\"activeOverlay()!; inputs: overlayInputs; injector: overlayInjector()\" />\n }\n </div>\n</div>\n", styles: [":host {\n display: block;\n flex: 1;\n min-height: 0;\n}\n\n.prism-canvas-stage {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 200px;\n padding: 32px;\n overflow: auto;\n\n /* The edge is outline + shadow, never border and never extra padding.\n plugin-visual-regression screenshots .demo-wrap, which is centred in\n this element; a border would shrink the content box by 2px and move\n that centre, shifting all 15 baselines in test-workspace/vrt/baseline\n without a single component having changed. Capture mode resets this\n element's padding but not its border, and this repo has no VRT runner\n to catch the drift. outline and box-shadow do not participate in\n layout, so the box is provably unchanged. Keep it that way.\n They do still paint, and outline-offset is negative, so the line lands\n inside the box: CAPTURE_STYLES in prism-capture.service.ts sets both\n to none, which is only safe because neither affects layout here. */\n outline: 1px solid var(--prism-stage-edge);\n outline-offset: -1px;\n background-color: var(--prism-stage);\n box-shadow:\n 0 1px 3px rgb(0 0 0 / 10%),\n 0 8px 24px -12px rgb(0 0 0 / 18%);\n transition: filter var(--dur-base);\n\n --prism-canvas-overlay-top: 12px;\n --prism-canvas-overlay-inline: 20px;\n}\n\n.prism-canvas-stage[data-rulers] {\n --prism-canvas-overlay-top: 28px;\n --prism-canvas-overlay-inline: 28px;\n}\n\n.stage-crosshair {\n position: absolute;\n inset: 0;\n pointer-events: none;\n opacity: 0;\n transition: opacity var(--dur-base);\n}\n\n.stage-crosshair.visible {\n opacity: 1;\n}\n\n.stage-crosshair::before,\n.stage-crosshair::after {\n position: absolute;\n content: '';\n background: color-mix(in srgb, var(--prism-primary) 20%, transparent);\n}\n\n.stage-crosshair::before {\n top: 50%;\n right: 0;\n left: 0;\n height: 1px;\n}\n\n.stage-crosshair::after {\n top: 0;\n bottom: 0;\n left: 50%;\n width: 1px;\n}\n\n.demo-wrap {\n position: relative;\n display: inline-block;\n transform: scale(var(--zoom, 1));\n transition: transform 0.18s;\n}\n\n.demo-wrap[data-canvas-layout='stretch'] {\n display: block;\n width: 100%;\n max-width: 800px;\n}\n\n/* max-width: none rather than min(800px, var(--prism-vp-w)): an explicitly\n requested width beats a layout default, and min() would silently cap a\n 1024 viewport at 800 on any component declaring canvasLayout:\n 'stretch'.\n\n container-type must stay confined to this rule. It implies contain: inline-size, which on the width:auto\n inline-block .demo-wrap is at rest would decouple its width from its\n contents, collapsing the box and shifting every baseline in\n test-workspace/vrt/baseline without a component having changed. Here\n the width is explicit, so containment costs nothing and is what makes\n a component's own @container rules respond. Media queries do not and\n cannot: they read the real browser viewport.\n\n grid + justify-items: center, and not text-align: center, which is the\n obvious way to centre an inline-level child and the wrong one. The\n showcase component is created through ViewContainerRef, so its host\n element carries none of this component's _ngcontent attribute and no\n scoped rule here can reach it to undo an inherited value. text-align\n would therefore leak all the way into the specimen and silently\n re-align its own text the moment the viewport is switched on. That is a\n canvas that lies about what it is showing. Centring at the container\n instead touches nothing inside.\n\n Shrinking is not a side effect of that choice, it is the resting\n behaviour preserved: at rest .demo-wrap is an inline-block that\n already shrink-wraps its child, so a block-level specimen sizes to its\n content here the same as with the constraint off. The implicit\n auto column still fills the declared width, so a component that asks\n for width: 100% (the canvasLayout: 'stretch' case) keeps getting the\n whole viewport. */\n.demo-wrap[data-viewport] {\n display: grid;\n\n /* flex: none is required. The stage is a flex container\n and this is its only item, so the default flex-shrink: 1 lets the\n box render narrower than the width just asked for whenever the\n canvas is the smaller of the two, and container-type below drops\n the automatic minimum size to zero, so nothing stops the shrink.\n The result is the one failure this whole feature cannot afford:\n the dimension line states 1024 while every @container rule in the\n specimen answers whatever the canvas happened to allow. The stage\n is overflow: auto, so refusing to shrink scrolls instead. */\n flex: none;\n\n /* safe center, not bare center. Centring is right while the specimen\n fits and wrong the moment it does not: bare centring spills an\n over-wide specimen equally past both edges, where a real viewport of\n that width clips at zero and scrolls in one direction. The safe\n keyword falls back to start only when the item overflows, which\n is the browser making a case distinction CSS cannot otherwise\n express. */\n justify-items: safe center;\n width: var(--prism-vp-w);\n max-width: none;\n container-type: inline-size;\n}\n\n/* Both grips derive their position arithmetically, because .demo-wrap is\n centred by the stage's own flexbox and then scaled in place by\n transform: scale(var(--zoom)): the transform preserves that centre, so\n each edge sits half the *painted* width (--prism-vp-w times --zoom) away\n from the middle. No measurement, no ResizeObserver, and nothing to\n fall out of step when either the width or the zoom changes.\n\n The grip's own [scale] input (in the template) undoes the same factor\n in the other direction: a pointer that has moved dx across the painted\n box must change --prism-vp-w by dx / zoom for the grip to stay under the\n cursor, so the directive multiplier is 2 / zoom on the right grip and\n -2 / zoom on the left, not the flat \u00B12 a permanent zoom of 1 would\n need. */\n.vp-grip {\n position: absolute;\n top: var(--prism-canvas-overlay-top, 12px);\n bottom: 0;\n left: calc(50% - var(--prism-vp-w) * var(--zoom, 1) / 2 - 4.5px);\n z-index: 4;\n display: grid;\n width: 9px;\n\n /* The element is the guide line's full-height track and must not be\n clickable. It runs from the overlay band to the\n bottom of the stage at z-index 4, straight over prism-canvas-bg-pill\n at z-index 2, so leaving it hit-testable made part of the pill's\n Reset button unreachable whenever a viewport was on. Only the bar\n below takes pointer events back. */\n pointer-events: none;\n background: transparent;\n}\n\n.vp-grip--end {\n right: calc(50% - var(--prism-vp-w) * var(--zoom, 1) / 2 - 4.5px);\n left: auto;\n}\n\n/* The part you grab, and the only part of the grip that takes pointer\n events back. It shares the guide line's grid cell (hence the matching\n grid-area) and is raised above it so the 1px rule does not paint a\n seam down the middle of the 3px bar.\n\n The padding is the hit box and the background is the bar: content-box\n sizing plus background-clip: content-box paints 3x34 while leaving\n 9x44 clickable, so the target is comfortable without the mark growing\n to match. It is still narrow for WCAG 2.5.8, which the Width chooser\n in the tools menu answers: the same widths are reachable there, and\n both grips take Home/End and the arrow keys. */\n.vp-grip__bar {\n z-index: 1;\n box-sizing: content-box;\n grid-area: 1 / 1;\n place-self: center center;\n width: 3px;\n height: 34px;\n padding: 5px 3px;\n pointer-events: auto;\n background: color-mix(in srgb, var(--prism-measure) 80%, transparent);\n background-clip: content-box;\n border-radius: 2px;\n transition: background var(--dur-fast);\n}\n\n/* The edge itself, carried the whole height of the stage. Without it a\n grip is a floating nub that says where you may pull but not what it is\n pulling: the line is what makes the constrained region legible as a\n region rather than as two loose handles. Kept faint because it crosses\n the specimen, so it has to stay readable as chrome. */\n.vp-grip::after {\n grid-area: 1 / 1;\n place-self: stretch center;\n width: 1px;\n content: '';\n background: color-mix(in srgb, var(--prism-measure) 75%, transparent);\n transition: background var(--dur-fast);\n}\n\n.vp-grip:hover::after,\n.vp-grip.active::after,\n.vp-grip:focus-visible::after {\n background: color-mix(in srgb, var(--prism-measure) 55%, transparent);\n}\n\n.vp-grip:hover .vp-grip__bar,\n.vp-grip.active .vp-grip__bar,\n.vp-grip:focus-visible .vp-grip__bar {\n background: var(--prism-measure);\n background-clip: content-box;\n}\n\n/* The ring goes on the bar, not on the host: the host is the full-height\n track, and outlining that would draw a rule the length of the stage\n for a control 34px tall. */\n.vp-grip:focus-visible {\n outline: none;\n}\n\n.vp-grip:focus-visible .vp-grip__bar {\n outline: 2px solid var(--prism-primary);\n outline-offset: 1px;\n}\n\n/* The third resident of the overlay band: pill left, dimension centre,\n rail right. It reads the same --prism-canvas-overlay-top the other two\n do, so switching rulers on moves all three together.\n\n Spans the painted width rather than sitting in the middle of it as a\n badge, and carries the same --prism-vp-w * --zoom the grips do so the\n three stay locked together. A badge states a number; a dimension line\n states which distance the number measures, and that is the whole\n claim this overlay makes. */\n.vp-dim {\n position: absolute;\n top: var(--prism-canvas-overlay-top, 12px);\n left: 50%;\n z-index: 4;\n display: flex;\n gap: var(--sp-3);\n align-items: center;\n width: calc(var(--prism-vp-w) * var(--zoom, 1));\n pointer-events: none;\n transform: translateX(-50%);\n}\n\n/* Two halves with the value in the gap, rather than one rule behind it:\n a line running under the digits would need a background plate to stay\n readable, and the plate is the badge this replaced. */\n.vp-dim__rule {\n position: relative;\n flex: 1;\n height: 1px;\n background: color-mix(in srgb, var(--prism-measure) 70%, transparent);\n}\n\n/* The end ticks. They are what make the rule read as a measurement of\n the span between them instead of as a divider laid across the canvas,\n and they mark the two edges the grips can be dragged to. */\n.vp-dim__rule::before {\n position: absolute;\n top: -3px;\n width: 1px;\n height: 7px;\n content: '';\n background: color-mix(in srgb, var(--prism-measure) 90%, transparent);\n}\n\n.vp-dim__rule:first-child::before {\n left: 0;\n}\n\n.vp-dim__rule:last-child::before {\n right: 0;\n}\n\n.vp-dim__v {\n flex: none;\n font-family: var(--font-mono);\n font-size: 9px;\n color: var(--prism-measure);\n white-space: nowrap;\n}\n", "\n [data-bg=\"dots\"] {\n background-image: radial-gradient(circle, var(--prism-dot) 1px, transparent 1px);\n background-size: 20px 20px;\n }\n\n [data-bg=\"plain\"] {\n background-image: none;\n }\n\n /* Flat, not dotted. \"light\" and \"dark\" name the surface a component was\n designed against, and their colour is absolute rather than a theme token,\n which makes them the values to declare for a screenshot baseline. Use\n \"dots\" for the grid. */\n /* These two also re-point --prism-measure. Other overlays use theme colours\n on theme surfaces, but these grounds ignore the theme: \"light\" stays\n near-white in the dark theme and \"dark\" stays near-black in the light one.\n The dark theme's magenta reaches only 2.85:1 on the light ground, under the\n 4.5:1 its 9px readout needs, so the measurement colour has to follow the\n ground, not the theme.\n\n --prism-measure-plate follows it for the same reason. It is the surface\n the measuring tool's value label paints itself on, so the label reads as a\n gap in the measurement line rather than a badge on top of it. At its\n --prism-stage fallback it would be the theme's stage under a re-pointed\n foreground: the light theme's magenta on the dark theme's stage reads\n 3.71:1, the dark theme's on white 3.08:1. Pinned to the ground the same way,\n the pairs reach 4.54:1 and 6.57:1. A color-mix against the ground would\n work too, but this file already states both grounds literally, and one\n mechanism per decision is enough.\n\n Literal values because this file is theme-independent, like the two\n grounds below. Keep them in step with --prism-measure in\n prism-default-theme.ts; semantic-colours.spec.ts fails if they drift. */\n [data-bg=\"light\"] {\n background-color: var(--prism-void-light, #f7f5fc);\n background-image: none;\n --prism-measure: #ca00c1;\n --prism-measure-plate: var(--prism-void-light, #f7f5fc);\n }\n [data-bg=\"dark\"] {\n background-color: var(--prism-void-dark, #07050f);\n background-image: none;\n --prism-measure: #f632ff;\n --prism-measure-plate: var(--prism-void-dark, #07050f);\n }\n\n /* \"transparent\" shares the checkerboard. In the app the checkerboard is\n already the UI's sign for \"no surface\", and a see-through stage would only\n show the shell behind it. Only CAPTURE_STYLES tells the two apart. */\n [data-bg=\"checker\"],\n [data-bg=\"transparent\"] {\n background-image:\n linear-gradient(45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--prism-border) 75%);\n background-size: 16px 16px;\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n }\n"] }]
365
+ args: [{ selector: 'prism-renderer', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [PrismCanvasRulersComponent, PrismCanvasBgPillComponent, PrismCanvasMeasureComponent, NgComponentOutlet, PrismResizerDirective], template: "<div\n class=\"prism-canvas-stage\"\n [attr.data-bg]=\"variantBg.effective()\"\n [attr.data-rulers]=\"canvasService.rulers() ? '' : null\"\n [attr.data-viewport]=\"viewportWidth() !== null ? '' : null\"\n [style.--prism-vp-w.px]=\"viewportWidth()\"\n [style.--zoom]=\"canvasService.zoom()\">\n @if (!capture.active()) {\n <div\n class=\"stage-crosshair\"\n [class.visible]=\"canvasService.guides()\"></div>\n <prism-canvas-rulers />\n @if (canvasService.measure()) {\n <prism-canvas-measure />\n }\n <prism-canvas-bg-pill />\n @if (viewportWidth() !== null) {\n <!-- Grips and the dimension line are siblings of .demo-wrap, never\n children of it: .demo-wrap is what plugin-visual-regression\n screenshots, and anything inside it composites into every baseline.\n They sit inside the capture guard for the same reason the rulers and\n the pill do. -->\n <span class=\"vp-dim\">\n <span class=\"vp-dim__rule\"></span>\n <span class=\"vp-dim__v\">{{ viewportWidth() }} px</span>\n <span class=\"vp-dim__rule\"></span>\n </span>\n <div\n class=\"vp-grip\"\n prismResizer\n axis=\"x\"\n [scale]=\"-2 / (canvasService.zoom() || 1)\"\n [min]=\"VIEWPORT_MIN\"\n [max]=\"VIEWPORT_MAX\"\n [value]=\"viewportWidth()!\"\n (valueChange)=\"onViewportResize($event)\"\n aria-label=\"Viewport width, left edge\"\n [attr.aria-valuenow]=\"viewportWidth()\"\n [attr.aria-valuemin]=\"VIEWPORT_MIN\"\n [attr.aria-valuemax]=\"VIEWPORT_MAX\">\n <span class=\"vp-grip__bar\"></span>\n </div>\n <div\n class=\"vp-grip vp-grip--end\"\n prismResizer\n axis=\"x\"\n [scale]=\"2 / (canvasService.zoom() || 1)\"\n [min]=\"VIEWPORT_MIN\"\n [max]=\"VIEWPORT_MAX\"\n [value]=\"viewportWidth()!\"\n (valueChange)=\"onViewportResize($event)\"\n aria-label=\"Viewport width, right edge\"\n [attr.aria-valuenow]=\"viewportWidth()\"\n [attr.aria-valuemin]=\"VIEWPORT_MIN\"\n [attr.aria-valuemax]=\"VIEWPORT_MAX\">\n <span class=\"vp-grip__bar\"></span>\n </div>\n }\n }\n\n <div\n class=\"demo-wrap\"\n [style.--zoom]=\"canvasService.zoom()\"\n [attr.data-prism-rendered]=\"renderedKey()\"\n [attr.data-prism-excluded]=\"excludedReason()\"\n [attr.data-canvas-layout]=\"canvasLayout()\"\n [attr.data-viewport]=\"viewportWidth() !== null ? '' : null\"\n [attr.data-pseudo]=\"forcedStates()\">\n <ng-container #outlet />\n @if (activeOverlay()) {\n <ng-container *ngComponentOutlet=\"activeOverlay()!; inputs: overlayInputs; injector: overlayInjector()\" />\n }\n </div>\n</div>\n", styles: [":host {\n display: block;\n flex: 1;\n min-height: 0;\n}\n\n.prism-canvas-stage {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 200px;\n padding: 32px;\n overflow: auto;\n\n /* The edge is outline + shadow, never border and never extra padding.\n plugin-visual-regression screenshots .demo-wrap, which is centred in\n this element; a border would shrink the content box by 2px and move\n that centre, shifting all 15 baselines in test-workspace/vrt/baseline\n without a single component having changed. Capture mode resets this\n element's padding but not its border, and this repo has no VRT runner\n to catch the drift. outline and box-shadow do not participate in\n layout, so the box is provably unchanged. Keep it that way.\n They do still paint, and outline-offset is negative, so the line lands\n inside the box: CAPTURE_STYLES in prism-capture.service.ts sets both\n to none, which is only safe because neither affects layout here. */\n outline: 1px solid var(--prism-stage-edge);\n outline-offset: -1px;\n background-color: var(--prism-stage);\n box-shadow:\n 0 1px 3px rgb(0 0 0 / 10%),\n 0 8px 24px -12px rgb(0 0 0 / 18%);\n transition: filter var(--dur-base);\n\n --prism-canvas-overlay-top: 12px;\n --prism-canvas-overlay-inline: 20px;\n}\n\n.prism-canvas-stage[data-rulers] {\n --prism-canvas-overlay-top: 28px;\n --prism-canvas-overlay-inline: 28px;\n}\n\n.stage-crosshair {\n position: absolute;\n inset: 0;\n pointer-events: none;\n opacity: 0;\n transition: opacity var(--dur-base);\n}\n\n.stage-crosshair.visible {\n opacity: 1;\n}\n\n.stage-crosshair::before,\n.stage-crosshair::after {\n position: absolute;\n content: '';\n background: color-mix(in srgb, var(--prism-primary) 20%, transparent);\n}\n\n.stage-crosshair::before {\n top: 50%;\n right: 0;\n left: 0;\n height: 1px;\n}\n\n.stage-crosshair::after {\n top: 0;\n bottom: 0;\n left: 50%;\n width: 1px;\n}\n\n.demo-wrap {\n position: relative;\n display: inline-block;\n transform: scale(var(--zoom, 1));\n transition: transform 0.18s;\n}\n\n.demo-wrap[data-canvas-layout='stretch'] {\n display: block;\n width: 100%;\n max-width: 800px;\n}\n\n/* max-width: none rather than min(800px, var(--prism-vp-w)): an explicitly\n requested width beats a layout default, and min() would silently cap a\n 1024 viewport at 800 on any component declaring canvasLayout:\n 'stretch'.\n\n container-type must stay confined to this rule. It implies contain: inline-size, which on the width:auto\n inline-block .demo-wrap is at rest would decouple its width from its\n contents, collapsing the box and shifting every baseline in\n test-workspace/vrt/baseline without a component having changed. Here\n the width is explicit, so containment costs nothing and is what makes\n a component's own @container rules respond. Media queries do not and\n cannot: they read the real browser viewport.\n\n grid + justify-items: center, and not text-align: center, which is the\n obvious way to centre an inline-level child and the wrong one. The\n showcase component is created through ViewContainerRef, so its host\n element carries none of this component's _ngcontent attribute and no\n scoped rule here can reach it to undo an inherited value. text-align\n would therefore leak all the way into the specimen and silently\n re-align its own text the moment the viewport is switched on. That is a\n canvas that lies about what it is showing. Centring at the container\n instead touches nothing inside.\n\n Shrinking is not a side effect of that choice, it is the resting\n behaviour preserved: at rest .demo-wrap is an inline-block that\n already shrink-wraps its child, so a block-level specimen sizes to its\n content here the same as with the constraint off. The implicit\n auto column still fills the declared width, so a component that asks\n for width: 100% (the canvasLayout: 'stretch' case) keeps getting the\n whole viewport. */\n.demo-wrap[data-viewport] {\n display: grid;\n\n /* flex: none is required. The stage is a flex container\n and this is its only item, so the default flex-shrink: 1 lets the\n box render narrower than the width just asked for whenever the\n canvas is the smaller of the two, and container-type below drops\n the automatic minimum size to zero, so nothing stops the shrink.\n The result is the one failure this whole feature cannot afford:\n the dimension line states 1024 while every @container rule in the\n specimen answers whatever the canvas happened to allow. The stage\n is overflow: auto, so refusing to shrink scrolls instead. */\n flex: none;\n\n /* safe center, not bare center. Centring is right while the specimen\n fits and wrong the moment it does not: bare centring spills an\n over-wide specimen equally past both edges, where a real viewport of\n that width clips at zero and scrolls in one direction. The safe\n keyword falls back to start only when the item overflows, which\n is the browser making a case distinction CSS cannot otherwise\n express. */\n justify-items: safe center;\n width: var(--prism-vp-w);\n max-width: none;\n container-type: inline-size;\n}\n\n/* Both grips derive their position arithmetically, because .demo-wrap is\n centred by the stage's own flexbox and then scaled in place by\n transform: scale(var(--zoom)): the transform preserves that centre, so\n each edge sits half the *painted* width (--prism-vp-w times --zoom) away\n from the middle. No measurement, no ResizeObserver, and nothing to\n fall out of step when either the width or the zoom changes.\n\n The grip's own [scale] input (in the template) undoes the same factor\n in the other direction: a pointer that has moved dx across the painted\n box must change --prism-vp-w by dx / zoom for the grip to stay under the\n cursor, so the directive multiplier is 2 / zoom on the right grip and\n -2 / zoom on the left, not the flat \u00B12 a permanent zoom of 1 would\n need. */\n.vp-grip {\n position: absolute;\n top: var(--prism-canvas-overlay-top, 12px);\n bottom: 0;\n left: calc(50% - var(--prism-vp-w) * var(--zoom, 1) / 2 - 4.5px);\n z-index: 4;\n display: grid;\n width: 9px;\n\n /* The element is the guide line's full-height track and must not be\n clickable. It runs from the overlay band to the\n bottom of the stage at z-index 4, straight over prism-canvas-bg-pill\n at z-index 2, so leaving it hit-testable made part of the pill's\n Reset button unreachable whenever a viewport was on. Only the bar\n below takes pointer events back. */\n pointer-events: none;\n background: transparent;\n}\n\n.vp-grip--end {\n right: calc(50% - var(--prism-vp-w) * var(--zoom, 1) / 2 - 4.5px);\n left: auto;\n}\n\n/* The part you grab, and the only part of the grip that takes pointer\n events back. It shares the guide line's grid cell (hence the matching\n grid-area) and is raised above it so the 1px rule does not paint a\n seam down the middle of the 3px bar.\n\n The padding is the hit box and the background is the bar: content-box\n sizing plus background-clip: content-box paints 3x34 while leaving\n 9x44 clickable, so the target is comfortable without the mark growing\n to match. It is still narrow for WCAG 2.5.8, which the Width chooser\n in the tools menu answers: the same widths are reachable there, and\n both grips take Home/End and the arrow keys. */\n.vp-grip__bar {\n z-index: 1;\n box-sizing: content-box;\n grid-area: 1 / 1;\n place-self: center center;\n width: 3px;\n height: 34px;\n padding: 5px 3px;\n pointer-events: auto;\n background: color-mix(in srgb, var(--prism-measure) 80%, transparent);\n background-clip: content-box;\n border-radius: 2px;\n transition: background var(--dur-fast);\n}\n\n/* The edge itself, carried the whole height of the stage. Without it a\n grip is a floating nub that says where you may pull but not what it is\n pulling: the line is what makes the constrained region legible as a\n region rather than as two loose handles. Kept faint because it crosses\n the specimen, so it has to stay readable as chrome. */\n.vp-grip::after {\n grid-area: 1 / 1;\n place-self: stretch center;\n width: 1px;\n content: '';\n background: color-mix(in srgb, var(--prism-measure) 75%, transparent);\n transition: background var(--dur-fast);\n}\n\n.vp-grip:hover::after,\n.vp-grip.active::after,\n.vp-grip:focus-visible::after {\n background: color-mix(in srgb, var(--prism-measure) 55%, transparent);\n}\n\n.vp-grip:hover .vp-grip__bar,\n.vp-grip.active .vp-grip__bar,\n.vp-grip:focus-visible .vp-grip__bar {\n background: var(--prism-measure);\n background-clip: content-box;\n}\n\n/* The ring goes on the bar, not on the host: the host is the full-height\n track, and outlining that would draw a rule the length of the stage\n for a control 34px tall. */\n.vp-grip:focus-visible {\n outline: none;\n}\n\n.vp-grip:focus-visible .vp-grip__bar {\n outline: 2px solid var(--prism-primary);\n outline-offset: 1px;\n}\n\n/* The third resident of the overlay band: pill left, dimension centre,\n rail right. It reads the same --prism-canvas-overlay-top the other two\n do, so switching rulers on moves all three together.\n\n Spans the painted width rather than sitting in the middle of it as a\n badge, and carries the same --prism-vp-w * --zoom the grips do so the\n three stay locked together. A badge states a number; a dimension line\n states which distance the number measures, and that is the whole\n claim this overlay makes. */\n.vp-dim {\n position: absolute;\n top: var(--prism-canvas-overlay-top, 12px);\n left: 50%;\n z-index: 4;\n display: flex;\n gap: var(--sp-3);\n align-items: center;\n width: calc(var(--prism-vp-w) * var(--zoom, 1));\n pointer-events: none;\n transform: translateX(-50%);\n}\n\n/* Two halves with the value in the gap, rather than one rule behind it:\n a line running under the digits would need a background plate to stay\n readable, and the plate is the badge this replaced. */\n.vp-dim__rule {\n position: relative;\n flex: 1;\n height: 1px;\n background: color-mix(in srgb, var(--prism-measure) 70%, transparent);\n}\n\n/* The end ticks. They are what make the rule read as a measurement of\n the span between them instead of as a divider laid across the canvas,\n and they mark the two edges the grips can be dragged to. */\n.vp-dim__rule::before {\n position: absolute;\n top: -3px;\n width: 1px;\n height: 7px;\n content: '';\n background: color-mix(in srgb, var(--prism-measure) 90%, transparent);\n}\n\n.vp-dim__rule:first-child::before {\n left: 0;\n}\n\n.vp-dim__rule:last-child::before {\n right: 0;\n}\n\n.vp-dim__v {\n flex: none;\n font-family: var(--font-mono);\n font-size: 9px;\n color: var(--prism-measure);\n white-space: nowrap;\n}\n", "\n [data-bg=\"dots\"] {\n background-image: radial-gradient(circle, var(--prism-dot) 1px, transparent 1px);\n background-size: 20px 20px;\n }\n\n [data-bg=\"plain\"] {\n background-image: none;\n }\n\n /* Flat, not dotted. \"light\" and \"dark\" name the surface a component was\n designed against, and their colour is absolute rather than a theme token,\n which makes them the values to declare for a screenshot baseline. Use\n \"dots\" for the grid. */\n /* These two also re-point --prism-measure. Other overlays use theme colours\n on theme surfaces, but these grounds ignore the theme: \"light\" stays\n near-white in the dark theme and \"dark\" stays near-black in the light one.\n The dark theme's magenta reaches only 2.85:1 on the light ground, under the\n 4.5:1 its 9px readout needs, so the measurement colour has to follow the\n ground, not the theme.\n\n --prism-measure-plate follows it for the same reason. It is the surface\n the measuring tool's value label paints itself on, so the label reads as a\n gap in the measurement line rather than a badge on top of it. At its\n --prism-stage fallback it would be the theme's stage under a re-pointed\n foreground: the light theme's magenta on the dark theme's stage reads\n 3.71:1, the dark theme's on white 3.08:1. Pinned to the ground the same way,\n the pairs reach 4.54:1 and 6.57:1. A color-mix against the ground would\n work too, but this file already states both grounds literally, and one\n mechanism per decision is enough.\n\n Literal values because this file is theme-independent, like the two\n grounds below. Keep them in step with --prism-measure in\n prism-default-theme.ts; semantic-colours.spec.ts fails if they drift. */\n [data-bg=\"light\"] {\n background-color: var(--prism-void-light, #f7f5fc);\n background-image: none;\n --prism-measure: #ca00c1;\n --prism-measure-plate: var(--prism-void-light, #f7f5fc);\n }\n [data-bg=\"dark\"] {\n background-color: var(--prism-void-dark, #07050f);\n background-image: none;\n --prism-measure: #f632ff;\n --prism-measure-plate: var(--prism-void-dark, #07050f);\n }\n\n /* \"transparent\" shares the checkerboard. In the app the checkerboard is\n already the UI's sign for \"no surface\", and a see-through stage would only\n show the shell behind it. Only CAPTURE_STYLES tells the two apart. */\n [data-bg=\"checker\"],\n [data-bg=\"transparent\"] {\n background-image:\n linear-gradient(45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--prism-border) 75%);\n background-size: 16px 16px;\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n }\n"] }]
344
366
  }], () => [], { outlet: [{ type: i0.ViewChild, args: ['outlet', { ...{
345
367
  read: ViewContainerRef
346
368
  }, isSignal: true }] }] }); })();
347
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 54 }); })();
369
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 55 }); })();
@@ -5,6 +5,13 @@
5
5
  * Shared by the Playground stage and the Overview cells: both project the same
6
6
  * developer-authored content, and the slot-selector handling is the one piece
7
7
  * of that neither wants to own twice.
8
+ *
9
+ * `createComponent` hands `projectableNodes[i]` to the i-th `<ng-content>` of
10
+ * the template and never looks at selectors, so the lists are ordered by the
11
+ * component's `ngContentSelectors` (`reflectComponentType`): `default` (or a
12
+ * plain string) goes to the `*` slot, every other key to the slot whose
13
+ * `select` it names. A key no slot takes is dropped and reported to
14
+ * `onUnmatchedKey`.
8
15
  */
9
- export declare function parseContentToNodes(content: string | Record<string, string>): Node[][];
16
+ export declare function parseContentToNodes(content: string | Record<string, string>, ngContentSelectors: readonly string[], onUnmatchedKey?: (key: string) => void): Node[][];
10
17
  //# sourceMappingURL=projectable-content.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"projectable-content.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/projectable-content.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,EAAE,EAAE,CAsCtF"}
1
+ {"version":3,"file":"projectable-content.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/projectable-content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,kBAAkB,EAAE,SAAS,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,EAAE,CAcrK"}
@@ -5,43 +5,54 @@
5
5
  * Shared by the Playground stage and the Overview cells: both project the same
6
6
  * developer-authored content, and the slot-selector handling is the one piece
7
7
  * of that neither wants to own twice.
8
+ *
9
+ * `createComponent` hands `projectableNodes[i]` to the i-th `<ng-content>` of
10
+ * the template and never looks at selectors, so the lists are ordered by the
11
+ * component's `ngContentSelectors` (`reflectComponentType`): `default` (or a
12
+ * plain string) goes to the `*` slot, every other key to the slot whose
13
+ * `select` it names. A key no slot takes is dropped and reported to
14
+ * `onUnmatchedKey`.
8
15
  */
9
16
  // SAFETY: `content` originates from `@Showcase({ variants: [{ content }] })`
10
17
  // in developer-authored source code. It is trusted by ng-prism's threat model
11
18
  // (see SECURITY.md). Sanitization would strip the Angular component/directive
12
19
  // selectors that variant content is meant to project.
13
- export function parseContentToNodes(content) {
14
- if (typeof content === 'string') {
15
- return [htmlToNodes(content)];
20
+ export function parseContentToNodes(content, ngContentSelectors, onUnmatchedKey) {
21
+ const byKey = typeof content === 'string' ? { default: content } : content;
22
+ const slotKeys = ngContentSelectors.map((selector) => (selector.trim() === '*' ? 'default' : selector.trim()));
23
+ for (const key of Object.keys(byKey)) {
24
+ if (!slotKeys.includes(key))
25
+ onUnmatchedKey?.(key);
16
26
  }
17
- const defaultNodes = content['default'] ? htmlToNodes(content['default']) : [];
18
- const result = [defaultNodes];
19
- for (const [selector, html] of Object.entries(content)) {
20
- if (selector === 'default')
21
- continue;
22
- const wrapper = document.createElement('div');
23
- // SAFETY: trusted developer-authored HTML, see SECURITY.md.
24
- wrapper.innerHTML = html;
25
- const nodes = [];
26
- for (const child of Array.from(wrapper.childNodes)) {
27
- const el = document.createElement('div');
28
- // SAFETY: trusted developer-authored HTML (see SECURITY.md).
29
- el.innerHTML = child.outerHTML ?? child.textContent ?? '';
30
- const projected = el.firstChild;
31
- if (projected && projected instanceof Element) {
32
- applySelector(projected, selector);
33
- nodes.push(projected);
34
- }
35
- else if (projected) {
36
- const span = document.createElement('span');
37
- applySelector(span, selector);
38
- span.textContent = child.textContent;
39
- nodes.push(span);
40
- }
27
+ return slotKeys.map((key) => {
28
+ const html = byKey[key];
29
+ if (html === undefined)
30
+ return [];
31
+ return key === 'default' ? htmlToNodes(html) : namedSlotNodes(html, key);
32
+ });
33
+ }
34
+ // SAFETY: trusted developer-authored HTML, see SECURITY.md.
35
+ function namedSlotNodes(html, selector) {
36
+ const wrapper = document.createElement('div');
37
+ wrapper.innerHTML = html;
38
+ const nodes = [];
39
+ for (const child of Array.from(wrapper.childNodes)) {
40
+ const el = document.createElement('div');
41
+ // SAFETY: trusted developer-authored HTML (see SECURITY.md).
42
+ el.innerHTML = child.outerHTML ?? child.textContent ?? '';
43
+ const projected = el.firstChild;
44
+ if (projected && projected instanceof Element) {
45
+ applySelector(projected, selector);
46
+ nodes.push(projected);
47
+ }
48
+ else if (projected) {
49
+ const span = document.createElement('span');
50
+ applySelector(span, selector);
51
+ span.textContent = child.textContent;
52
+ nodes.push(span);
41
53
  }
42
- result.push(nodes);
43
54
  }
44
- return result;
55
+ return nodes;
45
56
  }
46
57
  // SAFETY: see `parseContentToNodes` above and SECURITY.md. `html` is trusted
47
58
  // developer-authored variant content from the `@Showcase` decorator.
@@ -1,24 +1,38 @@
1
1
  import type { RuntimeComponent } from '../../plugin/plugin.types.js';
2
2
  import type { ForcedPseudoStates } from '../../shared/pseudo-state.type.js';
3
- import { type AppliedPseudoStates } from '../pseudo/pseudo-state-marker.js';
3
+ import type { AppliedPseudoStates } from '../pseudo/pseudo-state-marker.js';
4
4
  export interface RenderedKeyInput {
5
5
  component: RuntimeComponent | null;
6
6
  variantIndex: number;
7
+ /** The component-level state on the canvas, `null` when none is active. */
8
+ stateIndex: number | null;
7
9
  /** The rendered component's host element, `null` while nothing is rendered. */
8
10
  element: Element | null;
9
11
  /** The pseudo states the variant forces, `null` when it forces none. */
10
12
  pseudo: ForcedPseudoStates | null;
13
+ /** The active state's pseudo, `null` when no state applies to the variant. */
14
+ state: ForcedPseudoStates | null;
11
15
  /** What the canvas marker has the states in place for. */
12
16
  pseudoApplied: AppliedPseudoStates | null;
13
17
  }
14
18
  /**
15
19
  * The value of `data-prism-rendered` on `.demo-wrap`, or `null` while the
16
- * variant is not ready to be inspected.
20
+ * variant is not ready to be inspected: `<className>:<variantIndex>`, with
21
+ * `:<stateIndex>` while a component-level state is active.
17
22
  *
18
23
  * Screenshot runners wait on this marker, so a variant that forces pseudo
19
- * states only gets it once those exact states are in place on this exact
20
- * element. A runner then captures the forced state with no change on its side.
21
- * A variant without forced states is not held back.
24
+ * states, or has a state layer, only gets it once those exact objects are in
25
+ * place on this exact element. A runner then captures the forced state with no
26
+ * change on its side. A variant without forced states is not held back. The
27
+ * index is there for an opted-out variant too (`state` is `null`), so a runner
28
+ * waiting for its cell finds it.
22
29
  */
23
- export declare function resolveRenderedKey({ component, variantIndex, element, pseudo, pseudoApplied }: RenderedKeyInput): string | null;
30
+ export declare function resolveRenderedKey({ component, variantIndex, stateIndex, element, pseudo, state, pseudoApplied }: RenderedKeyInput): string | null;
31
+ /**
32
+ * The value of `data-prism-excluded` on `.demo-wrap`, or `null` when the cell
33
+ * shows what it was asked for: why the active state is not part of this
34
+ * variant, either because the variant opted out (`exclusion`) or because the
35
+ * marker found the state does not fit the rendered component.
36
+ */
37
+ export declare function resolveExcludedReason(stateIndex: number | null, exclusion: string | null, applied: AppliedPseudoStates | null): string | null;
24
38
  //# sourceMappingURL=rendered-key.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rendered-key.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/rendered-key.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAE,KAAK,mBAAmB,EAAkB,MAAM,kCAAkC,CAAC;AAE5F,MAAM,WAAW,gBAAgB;IAC7B,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,+EAA+E;IAC/E,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,wEAAwE;IACxE,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAClC,0DAA0D;IAC1D,aAAa,EAAE,mBAAmB,GAAG,IAAI,CAAC;CAC7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAI/H"}
1
+ {"version":3,"file":"rendered-key.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/rendered-key.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAG5E,MAAM,WAAW,gBAAgB;IAC7B,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,+EAA+E;IAC/E,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,wEAAwE;IACxE,MAAM,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAClC,8EAA8E;IAC9E,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACjC,0DAA0D;IAC1D,aAAa,EAAE,mBAAmB,GAAG,IAAI,CAAC;CAC7C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAOlJ;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAI7I"}
@@ -1,17 +1,34 @@
1
- import { forcesAnyState } from '../pseudo/pseudo-state-marker.js';
1
+ import { forcesAnyState } from '../pseudo/pseudo-targets.js';
2
2
  /**
3
3
  * The value of `data-prism-rendered` on `.demo-wrap`, or `null` while the
4
- * variant is not ready to be inspected.
4
+ * variant is not ready to be inspected: `<className>:<variantIndex>`, with
5
+ * `:<stateIndex>` while a component-level state is active.
5
6
  *
6
7
  * Screenshot runners wait on this marker, so a variant that forces pseudo
7
- * states only gets it once those exact states are in place on this exact
8
- * element. A runner then captures the forced state with no change on its side.
9
- * A variant without forced states is not held back.
8
+ * states, or has a state layer, only gets it once those exact objects are in
9
+ * place on this exact element. A runner then captures the forced state with no
10
+ * change on its side. A variant without forced states is not held back. The
11
+ * index is there for an opted-out variant too (`state` is `null`), so a runner
12
+ * waiting for its cell finds it.
10
13
  */
11
- export function resolveRenderedKey({ component, variantIndex, element, pseudo, pseudoApplied }) {
14
+ export function resolveRenderedKey({ component, variantIndex, stateIndex, element, pseudo, state, pseudoApplied }) {
12
15
  if (!element || !component)
13
16
  return null;
14
- if (forcesAnyState(pseudo) && (pseudoApplied?.root !== element || pseudoApplied.states !== pseudo))
17
+ if ((forcesAnyState(pseudo) || state !== null) && (pseudoApplied?.root !== element || pseudoApplied.states !== pseudo || pseudoApplied.state !== state))
15
18
  return null;
16
- return `${component.meta.className}:${variantIndex}`;
19
+ const key = `${component.meta.className}:${variantIndex}`;
20
+ return stateIndex === null ? key : `${key}:${stateIndex}`;
21
+ }
22
+ /**
23
+ * The value of `data-prism-excluded` on `.demo-wrap`, or `null` when the cell
24
+ * shows what it was asked for: why the active state is not part of this
25
+ * variant, either because the variant opted out (`exclusion`) or because the
26
+ * marker found the state does not fit the rendered component.
27
+ */
28
+ export function resolveExcludedReason(stateIndex, exclusion, applied) {
29
+ if (stateIndex === null)
30
+ return null;
31
+ if (exclusion !== null)
32
+ return exclusion;
33
+ return applied?.fit?.fits === false ? applied.fit.reason : null;
17
34
  }
@@ -35,7 +35,7 @@ export function generateSnippet(selector, inputs, values, explicitKeys, content,
35
35
  // that defaults to `true` has to show its explicit `false`.
36
36
  if (input.defaultValue === undefined && typeof value === 'boolean' && !value)
37
37
  continue;
38
- pushAttribute(attributes, input.name, value);
38
+ pushAttribute(attributes, input.alias ?? input.name, value);
39
39
  }
40
40
  for (const [name, value] of Object.entries(values)) {
41
41
  if (processed.has(name))
@@ -112,7 +112,7 @@ function generateDirectiveSnippet(selector, inputs, values, explicitKeys, conten
112
112
  // that defaults to `true` has to show its explicit `false`.
113
113
  if (input.defaultValue === undefined && typeof value === 'boolean' && !value)
114
114
  continue;
115
- pushAttribute(attributes, input.name, value);
115
+ pushAttribute(attributes, input.alias ?? input.name, value);
116
116
  }
117
117
  for (const [name, value] of Object.entries(values)) {
118
118
  if (processed.has(name))
@@ -124,7 +124,9 @@ function generateDirectiveSnippet(selector, inputs, values, explicitKeys, conten
124
124
  pushAttribute(attributes, name, value);
125
125
  }
126
126
  const projectedContent = content ?? (typeof values['__prismContent__'] === 'string' ? values['__prismContent__'] : undefined);
127
- const allAttrs = [...hostAttrs, directiveAttr, ...attributes].join(' ');
127
+ // An input aliased to the directive's own selector already puts the directive on the host.
128
+ const selectorAttrs = attributes.some((a) => a.startsWith(`${directiveAttr}=`) || a.startsWith(`[${directiveAttr}]=`)) ? [] : [directiveAttr];
129
+ const allAttrs = [...hostAttrs, ...selectorAttrs, ...attributes].join(' ');
128
130
  const contentHtml = resolveContentHtml(projectedContent);
129
131
  if (!contentHtml) {
130
132
  if (!allAttrs)
@@ -132,7 +134,7 @@ function generateDirectiveSnippet(selector, inputs, values, explicitKeys, conten
132
134
  const singleLine = `<${tag} ${allAttrs} />`;
133
135
  if (singleLine.length <= 80)
134
136
  return singleLine;
135
- const indented = [...hostAttrs, directiveAttr, ...attributes].map((a) => ` ${a}`).join('\n');
137
+ const indented = [...hostAttrs, ...selectorAttrs, ...attributes].map((a) => ` ${a}`).join('\n');
136
138
  return `<${tag}\n${indented} />`;
137
139
  }
138
140
  const openTag = `<${tag} ${allAttrs}>`;
@@ -140,7 +142,7 @@ function generateDirectiveSnippet(selector, inputs, values, explicitKeys, conten
140
142
  const singleLine = `${openTag}${contentHtml}${closeTag}`;
141
143
  if (singleLine.length <= 80)
142
144
  return singleLine;
143
- const allAttrsList = [...hostAttrs, directiveAttr, ...attributes];
145
+ const allAttrsList = [...hostAttrs, ...selectorAttrs, ...attributes];
144
146
  if (allAttrsList.length <= 2) {
145
147
  return `${openTag}\n ${contentHtml}\n${closeTag}`;
146
148
  }