@ng-prism/core 22.4.1 → 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 (131) hide show
  1. package/README.md +2 -0
  2. package/dist/app/canvas/prism-canvas-toolbar.component.d.ts +33 -2
  3. package/dist/app/canvas/prism-canvas-toolbar.component.d.ts.map +1 -1
  4. package/dist/app/canvas/prism-canvas-toolbar.component.js +135 -28
  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 +22 -4
  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 +29 -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 +145 -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-selector.d.ts +24 -0
  44. package/dist/app/pseudo/pseudo-selector.d.ts.map +1 -0
  45. package/dist/app/pseudo/pseudo-selector.js +136 -0
  46. package/dist/app/pseudo/pseudo-state-marker.d.ts +66 -0
  47. package/dist/app/pseudo/pseudo-state-marker.d.ts.map +1 -0
  48. package/dist/app/pseudo/pseudo-state-marker.js +165 -0
  49. package/dist/app/pseudo/pseudo-stylesheets.d.ts +49 -0
  50. package/dist/app/pseudo/pseudo-stylesheets.d.ts.map +1 -0
  51. package/dist/app/pseudo/pseudo-stylesheets.js +137 -0
  52. package/dist/app/pseudo/pseudo-targets.d.ts +11 -0
  53. package/dist/app/pseudo/pseudo-targets.d.ts.map +1 -0
  54. package/dist/app/pseudo/pseudo-targets.js +46 -0
  55. package/dist/app/pseudo/state-fit.d.ts +28 -0
  56. package/dist/app/pseudo/state-fit.d.ts.map +1 -0
  57. package/dist/app/pseudo/state-fit.js +97 -0
  58. package/dist/app/renderer/input-bindings.d.ts +16 -0
  59. package/dist/app/renderer/input-bindings.d.ts.map +1 -0
  60. package/dist/app/renderer/input-bindings.js +21 -0
  61. package/dist/app/renderer/known-inputs.d.ts +8 -1
  62. package/dist/app/renderer/known-inputs.d.ts.map +1 -1
  63. package/dist/app/renderer/known-inputs.js +13 -5
  64. package/dist/app/renderer/prism-renderer.component.d.ts +18 -1
  65. package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
  66. package/dist/app/renderer/prism-renderer.component.js +68 -22
  67. package/dist/app/renderer/projectable-content.d.ts +8 -1
  68. package/dist/app/renderer/projectable-content.d.ts.map +1 -1
  69. package/dist/app/renderer/projectable-content.js +40 -29
  70. package/dist/app/renderer/rendered-key.d.ts +38 -0
  71. package/dist/app/renderer/rendered-key.d.ts.map +1 -0
  72. package/dist/app/renderer/rendered-key.js +34 -0
  73. package/dist/app/renderer/snippet-generator.js +7 -5
  74. package/dist/app/services/prism-capture.service.d.ts +2 -0
  75. package/dist/app/services/prism-capture.service.d.ts.map +1 -1
  76. package/dist/app/services/prism-capture.service.js +25 -0
  77. package/dist/app/services/prism-pseudo-state.service.d.ts +93 -0
  78. package/dist/app/services/prism-pseudo-state.service.d.ts.map +1 -0
  79. package/dist/app/services/prism-pseudo-state.service.js +155 -0
  80. package/dist/app/services/prism-renderer.service.d.ts +17 -0
  81. package/dist/app/services/prism-renderer.service.d.ts.map +1 -1
  82. package/dist/app/services/prism-renderer.service.js +37 -2
  83. package/dist/app/services/prism-url-state.service.d.ts.map +1 -1
  84. package/dist/app/services/prism-url-state.service.js +39 -8
  85. package/dist/app/theme/prism-default-theme.d.ts.map +1 -1
  86. package/dist/app/theme/prism-default-theme.js +6 -5
  87. package/dist/builder/config-loader/deprecated-options.d.ts +7 -0
  88. package/dist/builder/config-loader/deprecated-options.d.ts.map +1 -0
  89. package/dist/builder/config-loader/deprecated-options.js +23 -0
  90. package/dist/builder/manifest/runtime-manifest.generator.d.ts.map +1 -1
  91. package/dist/builder/manifest/runtime-manifest.generator.js +3 -2
  92. package/dist/builder/scanner/component.scanner.d.ts.map +1 -1
  93. package/dist/builder/scanner/component.scanner.js +27 -0
  94. package/dist/builder/scanner/entry-point-discovery.d.ts +5 -0
  95. package/dist/builder/scanner/entry-point-discovery.d.ts.map +1 -1
  96. package/dist/builder/scanner/entry-point-discovery.js +14 -0
  97. package/dist/builder/scanner/entry-point.scanner.d.ts +3 -1
  98. package/dist/builder/scanner/entry-point.scanner.d.ts.map +1 -1
  99. package/dist/builder/scanner/entry-point.scanner.js +7 -6
  100. package/dist/builder/scanner/input.extractor.d.ts.map +1 -1
  101. package/dist/builder/scanner/input.extractor.js +22 -3
  102. package/dist/builder/scanner/pseudo-states.d.ts +12 -0
  103. package/dist/builder/scanner/pseudo-states.d.ts.map +1 -0
  104. package/dist/builder/scanner/pseudo-states.js +58 -0
  105. package/dist/builder/scanner/scanner.d.ts +2 -2
  106. package/dist/builder/scanner/scanner.d.ts.map +1 -1
  107. package/dist/builder/scanner/scanner.js +16 -6
  108. package/dist/builder/scanner/showcase-diagnostics.js +9 -9
  109. package/dist/builder/scanner/showcase-states.d.ts +17 -0
  110. package/dist/builder/scanner/showcase-states.d.ts.map +1 -0
  111. package/dist/builder/scanner/showcase-states.js +58 -0
  112. package/dist/builder/scanner/source-file-cache.d.ts +26 -0
  113. package/dist/builder/scanner/source-file-cache.d.ts.map +1 -0
  114. package/dist/builder/scanner/source-file-cache.js +60 -0
  115. package/dist/builder/shared/prism-pipeline.d.ts.map +1 -1
  116. package/dist/builder/shared/prism-pipeline.js +28 -13
  117. package/dist/decorator/index.d.ts +1 -1
  118. package/dist/decorator/index.d.ts.map +1 -1
  119. package/dist/decorator/showcase.types.d.ts +33 -0
  120. package/dist/decorator/showcase.types.d.ts.map +1 -1
  121. package/dist/plugin/index.d.ts +3 -1
  122. package/dist/plugin/index.d.ts.map +1 -1
  123. package/dist/plugin/index.js +1 -0
  124. package/dist/plugin/plugin.types.d.ts +53 -7
  125. package/dist/plugin/plugin.types.d.ts.map +1 -1
  126. package/dist/shared/pseudo-state.type.d.ts +22 -0
  127. package/dist/shared/pseudo-state.type.d.ts.map +1 -0
  128. package/dist/shared/pseudo-state.type.js +2 -0
  129. package/package.json +1 -1
  130. package/src/builder/build/schema.json +6 -6
  131. package/src/builder/serve/schema.json +6 -6
@@ -0,0 +1,46 @@
1
+ import { PSEUDO_STATES } from '../../shared/pseudo-state.type.js';
2
+ /** True when `states` forces at least one state on at least one target. */
3
+ export function forcesAnyState(states) {
4
+ return !!states && PSEUDO_STATES.some((state) => hasTarget(states[state]));
5
+ }
6
+ /** True when `target` names something: `true`, or a selector that is not blank. */
7
+ export function hasTarget(target) {
8
+ if (target === true)
9
+ return true;
10
+ if (typeof target === 'string')
11
+ return target.trim() !== '';
12
+ return Array.isArray(target) && target.some((selector) => typeof selector === 'string' && selector.trim() !== '');
13
+ }
14
+ const warnedSelectors = new Set();
15
+ /**
16
+ * The elements under `root` a target names. `root` counts: a selector can name
17
+ * the component's host, and `true` includes it.
18
+ */
19
+ export function resolveTargets(root, target) {
20
+ const targets = new Set();
21
+ if (target === undefined)
22
+ return targets;
23
+ if (target === true) {
24
+ targets.add(root);
25
+ for (const el of Array.from(root.querySelectorAll('*')))
26
+ targets.add(el);
27
+ return targets;
28
+ }
29
+ for (const selector of typeof target === 'string' ? [target] : target) {
30
+ if (typeof selector !== 'string' || selector.trim() === '')
31
+ continue;
32
+ try {
33
+ if (root.matches(selector))
34
+ targets.add(root);
35
+ for (const el of Array.from(root.querySelectorAll(selector)))
36
+ targets.add(el);
37
+ }
38
+ catch {
39
+ if (!warnedSelectors.has(selector)) {
40
+ warnedSelectors.add(selector);
41
+ console.warn(`[ng-prism] "${selector}" in forced pseudo states is not a valid selector, skipping it.`);
42
+ }
43
+ }
44
+ }
45
+ return targets;
46
+ }
@@ -0,0 +1,28 @@
1
+ import { type ForcedPseudoStates, type PseudoState, type PseudoTarget } from '../../shared/pseudo-state.type.js';
2
+ /** Whether a state can be forced on a rendered component, and if not, why. */
3
+ export type StateFit = {
4
+ fits: true;
5
+ } | {
6
+ fits: false;
7
+ reason: string;
8
+ };
9
+ /** `true` is the whole component, a selector list reads as its selectors joined with `, `. */
10
+ export declare function describeTarget(target: PseudoTarget): string;
11
+ /**
12
+ * The subset of `elements` the browser could put into `state`: what a user
13
+ * could focus, or hover, or press. Forcing a state on anything else would show
14
+ * something the component can never be in.
15
+ *
16
+ * `focus-within` is not filtered. Whether some descendant takes focus cannot be
17
+ * told without a browser API for focusability.
18
+ */
19
+ export declare function reachableTargets(root: Element, state: PseudoState, elements: Iterable<Element>): Element[];
20
+ /**
21
+ * Fits when every pseudo-class of `pseudo` has at least one reachable target
22
+ * under `root`. The reason names the first pseudo-class, in
23
+ * {@link PSEUDO_STATES} order, that does not. Focus on the whole component is
24
+ * decided on what could take focus ({@link fitCandidates}); which elements get
25
+ * the class is not affected.
26
+ */
27
+ export declare function evaluateStateFit(root: Element, pseudo: ForcedPseudoStates): StateFit;
28
+ //# sourceMappingURL=state-fit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-fit.d.ts","sourceRoot":"","sources":["../../../src/app/pseudo/state-fit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAAiB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAGhI,8EAA8E;AAC9E,MAAM,MAAM,QAAQ,GAAG;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAG3D;AA4BD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,OAAO,EAAE,CAc1G;AAyBD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,kBAAkB,GAAG,QAAQ,CAkBpF"}
@@ -0,0 +1,97 @@
1
+ import { PSEUDO_STATES } from '../../shared/pseudo-state.type.js';
2
+ import { forcesAnyState, resolveTargets } from './pseudo-targets.js';
3
+ /** `true` is the whole component, a selector list reads as its selectors joined with `, `. */
4
+ export function describeTarget(target) {
5
+ if (target === true)
6
+ return 'the component';
7
+ return typeof target === 'string' ? target : target.join(', ');
8
+ }
9
+ function isDisabled(el) {
10
+ return el.matches(':disabled');
11
+ }
12
+ /** Under `[inert]` within the component: an `inert` outside `root` is not the component's doing. */
13
+ function isInert(root, el) {
14
+ const inert = el.closest('[inert]');
15
+ return inert !== null && root.contains(inert);
16
+ }
17
+ /**
18
+ * Whether `el` or something inside it receives the pointer. `pointer-events`
19
+ * inherits, so a subtree of `none` is unreachable unless a descendant sets it
20
+ * back, and then the browser hands that descendant the hover.
21
+ */
22
+ function takesPointer(el, known) {
23
+ let reachable = known.get(el);
24
+ if (reachable === undefined) {
25
+ reachable = getComputedStyle(el).pointerEvents !== 'none' || Array.from(el.children).some((child) => takesPointer(child, known));
26
+ known.set(el, reachable);
27
+ }
28
+ return reachable;
29
+ }
30
+ /**
31
+ * The subset of `elements` the browser could put into `state`: what a user
32
+ * could focus, or hover, or press. Forcing a state on anything else would show
33
+ * something the component can never be in.
34
+ *
35
+ * `focus-within` is not filtered. Whether some descendant takes focus cannot be
36
+ * told without a browser API for focusability.
37
+ */
38
+ export function reachableTargets(root, state, elements) {
39
+ switch (state) {
40
+ case 'focus':
41
+ case 'focus-visible':
42
+ return Array.from(elements).filter((el) => !isDisabled(el) && !isInert(root, el));
43
+ case 'hover':
44
+ case 'active': {
45
+ const known = new Map();
46
+ return Array.from(elements).filter((el) => takesPointer(el, known));
47
+ }
48
+ case 'focus-within':
49
+ return Array.from(elements);
50
+ }
51
+ }
52
+ /** What can take focus at all, whether or not it is disabled or inert now. */
53
+ const FOCUSABLE = 'button, input, select, textarea, a[href], area[href], summary, iframe, [tabindex], [contenteditable]:not([contenteditable="false"])';
54
+ /**
55
+ * The targets the fit of `state` is decided on. For focus on the whole
56
+ * component (`true`) that is what could take focus at all: the host is never
57
+ * `:disabled`, so a component whose only focusable element is disabled would
58
+ * otherwise fit, and show the plain default under a focus label. A component
59
+ * with nothing focusable in it keeps every element, as before.
60
+ */
61
+ function fitCandidates(state, target, targets) {
62
+ if (target !== true || (state !== 'focus' && state !== 'focus-visible'))
63
+ return targets;
64
+ const focusable = Array.from(targets).filter((el) => el.matches(FOCUSABLE));
65
+ return focusable.length > 0 ? new Set(focusable) : targets;
66
+ }
67
+ /** Why `elements`, all of them unreachable for `state`, are: the first applicable of disabled, inert and pointer-events. */
68
+ function unreachableReason(state, elements) {
69
+ if (state === 'hover' || state === 'active')
70
+ return 'has pointer-events: none';
71
+ return Array.from(elements).some(isDisabled) ? 'is disabled' : 'is inert';
72
+ }
73
+ /**
74
+ * Fits when every pseudo-class of `pseudo` has at least one reachable target
75
+ * under `root`. The reason names the first pseudo-class, in
76
+ * {@link PSEUDO_STATES} order, that does not. Focus on the whole component is
77
+ * decided on what could take focus ({@link fitCandidates}); which elements get
78
+ * the class is not affected.
79
+ */
80
+ export function evaluateStateFit(root, pseudo) {
81
+ if (!forcesAnyState(pseudo))
82
+ return { fits: false, reason: 'forces no state' };
83
+ for (const state of PSEUDO_STATES) {
84
+ const target = pseudo[state];
85
+ // Like the marker, which gives an empty selector no element to mark.
86
+ if (target === undefined || !forcesAnyState({ [state]: target }))
87
+ continue;
88
+ const targets = resolveTargets(root, target);
89
+ const label = describeTarget(target);
90
+ if (targets.size === 0)
91
+ return { fits: false, reason: `${label} matches nothing` };
92
+ const candidates = fitCandidates(state, target, targets);
93
+ if (reachableTargets(root, state, candidates).length === 0)
94
+ return { fits: false, reason: `${label} ${unreachableReason(state, candidates)}` };
95
+ }
96
+ return { fits: true };
97
+ }
@@ -0,0 +1,16 @@
1
+ import type { RuntimeComponent } from '../../plugin/plugin.types.js';
2
+ /** Synthetic input the manifest generator emits on directive wrapper classes
3
+ * to project per-variant `content` text into the host element. The directive
4
+ * itself doesn't declare it, so it isn't in `meta.inputs`, and code that
5
+ * validates / filters inputs against that list must allow this key through.
6
+ */
7
+ export declare const PRISM_CONTENT_INPUT = "__prismContent__";
8
+ /**
9
+ * The inputs a variant may set, by class member name, each mapped to the name
10
+ * `ComponentRef.setInput` takes. Angular resolves that by the public name, so a
11
+ * component's aliased input maps to its alias. A directive renders inside the
12
+ * generated `__PrismHost` wrapper, which declares the inputs under their member
13
+ * names and binds the aliases itself.
14
+ */
15
+ export declare function buildInputBindings(comp: RuntimeComponent): Map<string, string>;
16
+ //# sourceMappingURL=input-bindings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-bindings.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/input-bindings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ9E"}
@@ -0,0 +1,21 @@
1
+ /** Synthetic input the manifest generator emits on directive wrapper classes
2
+ * to project per-variant `content` text into the host element. The directive
3
+ * itself doesn't declare it, so it isn't in `meta.inputs`, and code that
4
+ * validates / filters inputs against that list must allow this key through.
5
+ */
6
+ export const PRISM_CONTENT_INPUT = '__prismContent__';
7
+ /**
8
+ * The inputs a variant may set, by class member name, each mapped to the name
9
+ * `ComponentRef.setInput` takes. Angular resolves that by the public name, so a
10
+ * component's aliased input maps to its alias. A directive renders inside the
11
+ * generated `__PrismHost` wrapper, which declares the inputs under their member
12
+ * names and binds the aliases itself.
13
+ */
14
+ export function buildInputBindings(comp) {
15
+ const isDirective = comp.meta.componentMeta.isDirective;
16
+ const bindings = new Map(comp.meta.inputs.map((i) => [i.name, isDirective ? i.name : (i.alias ?? i.name)]));
17
+ if (isDirective) {
18
+ bindings.set(PRISM_CONTENT_INPUT, PRISM_CONTENT_INPUT);
19
+ }
20
+ return bindings;
21
+ }
@@ -5,5 +5,12 @@ import type { RuntimeComponent } from '../../plugin/plugin.types.js';
5
5
  * validates / filters inputs against that list must allow this key through.
6
6
  */
7
7
  export declare const PRISM_CONTENT_INPUT = "__prismContent__";
8
- export declare function buildKnownInputs(comp: RuntimeComponent): Set<string>;
8
+ /**
9
+ * The inputs a variant may set, by class member name, each mapped to the name
10
+ * `ComponentRef.setInput` takes. Angular resolves that by the public name, so a
11
+ * component's aliased input maps to its alias. A directive renders inside the
12
+ * generated `__PrismHost` wrapper, which declares the inputs under their member
13
+ * names and binds the aliases itself.
14
+ */
15
+ export declare function buildInputBindings(comp: RuntimeComponent): Map<string, string>;
9
16
  //# sourceMappingURL=known-inputs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"known-inputs.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/known-inputs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAEtD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,CAOpE"}
1
+ {"version":3,"file":"known-inputs.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/known-inputs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,qBAAqB,CAAC;AAEtD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ9E"}
@@ -4,10 +4,18 @@
4
4
  * validates / filters inputs against that list must allow this key through.
5
5
  */
6
6
  export const PRISM_CONTENT_INPUT = '__prismContent__';
7
- export function buildKnownInputs(comp) {
8
- const known = new Set(comp.meta.inputs.map((i) => i.name));
9
- if (comp.meta.componentMeta.isDirective) {
10
- known.add(PRISM_CONTENT_INPUT);
7
+ /**
8
+ * The inputs a variant may set, by class member name, each mapped to the name
9
+ * `ComponentRef.setInput` takes. Angular resolves that by the public name, so a
10
+ * component's aliased input maps to its alias. A directive renders inside the
11
+ * generated `__PrismHost` wrapper, which declares the inputs under their member
12
+ * names and binds the aliases itself.
13
+ */
14
+ export function buildInputBindings(comp) {
15
+ const isDirective = comp.meta.componentMeta.isDirective;
16
+ const bindings = new Map(comp.meta.inputs.map((i) => [i.name, isDirective ? i.name : (i.alias ?? i.name)]));
17
+ if (isDirective) {
18
+ bindings.set(PRISM_CONTENT_INPUT, PRISM_CONTENT_INPUT);
11
19
  }
12
- return known;
20
+ return bindings;
13
21
  }
@@ -11,6 +11,7 @@ export declare class PrismRendererComponent {
11
11
  protected readonly canvasService: PrismCanvasService;
12
12
  protected readonly capture: PrismCaptureService;
13
13
  protected readonly variantBg: PrismVariantBgService;
14
+ private readonly pseudoState;
14
15
  /**
15
16
  * The width the canvas is actually constrained to, `null` for unconstrained.
16
17
  *
@@ -44,6 +45,7 @@ export declare class PrismRendererComponent {
44
45
  private readonly rendererHooks;
45
46
  private readonly panelService;
46
47
  private readonly pluginService;
48
+ private readonly pseudoMarker;
47
49
  private readonly outlet;
48
50
  private componentRef;
49
51
  private outputSubscriptions;
@@ -51,8 +53,23 @@ export declare class PrismRendererComponent {
51
53
  private isRenderPage;
52
54
  private readonly overlayCache;
53
55
  readonly activeOverlay: import("@angular/core").WritableSignal<Type<unknown> | null>;
54
- /** Identifier of the currently rendered component/variant (for audit tooling / e2e). */
56
+ /**
57
+ * Identifier of the currently rendered component/variant, and of the active state.
58
+ * Held back until the forced pseudo states and the state are in place, see {@link resolveRenderedKey}.
59
+ */
55
60
  protected readonly renderedKey: import("@angular/core").Signal<string | null>;
61
+ /**
62
+ * Why the active state is not part of what the canvas shows, `null` when it is
63
+ * or none is active. A runner reads it next to `data-prism-rendered` and
64
+ * reports the cell as excluded rather than taking a picture of it.
65
+ */
66
+ protected readonly excludedReason: import("@angular/core").Signal<string | null>;
67
+ /**
68
+ * The states forced on the canvas, space-separated, `null` for none. Capture
69
+ * mode keys the room it leaves for focus rings on it. The active state's
70
+ * pseudo-classes count only while it fits.
71
+ */
72
+ protected readonly forcedStates: import("@angular/core").Signal<string | null>;
56
73
  /** Resolved canvas layout for the active variant. Variant overrides component config; defaults to 'fit'. */
57
74
  protected readonly canvasLayout: import("@angular/core").Signal<import("../../shared/canvas-layout.type.js").CanvasLayout>;
58
75
  protected readonly overlayInputs: {
@@ -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;AAWvB,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAG3E,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAGjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;;AAMhF,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;IAE7D;;;;;;;;;;;;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;IAE5D,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,wFAAwF;IACxF,SAAS,CAAC,QAAQ,CAAC,WAAW,gDAQ3B;IACH,4GAA4G;IAC5G,SAAS,CAAC,QAAQ,CAAC,YAAY,4FAO5B;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa;;MAA6C;IAC7E,SAAS,CAAC,QAAQ,CAAC,eAAe,yFAI/B;;IAgFH,OAAO,CAAC,eAAe;IA8EvB,OAAO,CAAC,OAAO;yCAjPN,sBAAsB;2CAAtB,sBAAsB;CAgQlC"}
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,5 +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';
2
+ import { ChangeDetectionStrategy, Component, computed, DestroyRef, effect, ElementRef, inject, Injector, reflectComponentType, signal, untracked, viewChild, ViewContainerRef } from '@angular/core';
3
3
  import { VIEWPORT_MAX, VIEWPORT_MIN } from '../../shared/viewport.type.js';
4
4
  import { CANVAS_BG_STYLES } from '../canvas/canvas-bg.styles.js';
5
5
  import { PrismCanvasBgPillComponent } from '../canvas/prism-canvas-bg-pill.component.js';
@@ -8,6 +8,7 @@ import { PrismCanvasRulersComponent } from '../canvas/prism-canvas-rulers.compon
8
8
  import { snapViewportWidth } from '../canvas/viewport-snap.js';
9
9
  import { PrismResizerDirective } from '../directives/prism-resizer.directive.js';
10
10
  import { BUILTIN_PANELS } from '../panels/builtin-panels.js';
11
+ import { appliedStateNames } from '../pseudo/pseudo-state-marker.js';
11
12
  import { PrismCanvasService } from '../services/prism-canvas.service.js';
12
13
  import { PrismCaptureService } from '../services/prism-capture.service.js';
13
14
  import { PrismEventLogService } from '../services/prism-event-log.service.js';
@@ -15,12 +16,14 @@ import { PrismManifestService } from '../services/prism-manifest.service.js';
15
16
  import { PrismNavigationService } from '../services/prism-navigation.service.js';
16
17
  import { PrismPanelService } from '../services/prism-panel.service.js';
17
18
  import { PrismPluginService } from '../services/prism-plugin.service.js';
19
+ import { PrismPseudoStateService } from '../services/prism-pseudo-state.service.js';
18
20
  import { PrismRendererService } from '../services/prism-renderer.service.js';
19
21
  import { PrismVariantBgService } from '../services/prism-variant-bg.service.js';
20
22
  import { PRISM_RENDERER_HOOKS } from '../tokens/prism-tokens.js';
21
- import { buildKnownInputs } from './known-inputs.js';
23
+ import { buildInputBindings } from './input-bindings.js';
22
24
  import { resolveOverlay } from './overlay-resolver.js';
23
25
  import { parseContentToNodes } from './projectable-content.js';
26
+ import { resolveExcludedReason, resolveRenderedKey } from './rendered-key.js';
24
27
  import * as i0 from "@angular/core";
25
28
  const _c0 = ["outlet"];
26
29
  function PrismRendererComponent_Conditional_1_Conditional_2_Template(rf, ctx) { if (rf & 1) {
@@ -82,6 +85,7 @@ export class PrismRendererComponent {
82
85
  canvasService = inject(PrismCanvasService);
83
86
  capture = inject(PrismCaptureService);
84
87
  variantBg = inject(PrismVariantBgService);
88
+ pseudoState = inject(PrismPseudoStateService);
85
89
  /**
86
90
  * The width the canvas is actually constrained to, `null` for unconstrained.
87
91
  *
@@ -120,6 +124,7 @@ export class PrismRendererComponent {
120
124
  });
121
125
  panelService = inject(PrismPanelService);
122
126
  pluginService = inject(PrismPluginService);
127
+ pseudoMarker = this.pseudoState.createMarker();
123
128
  outlet = viewChild.required('outlet', { ...(ngDevMode ? { debugName: "outlet" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
124
129
  componentRef = null;
125
130
  outputSubscriptions = [];
@@ -128,17 +133,40 @@ export class PrismRendererComponent {
128
133
  overlayCache = new Map();
129
134
  activeOverlay = signal(null, /* @ts-ignore */
130
135
  ...(ngDevMode ? [{ debugName: "activeOverlay" }] : /* istanbul ignore next */ []));
131
- /** Identifier of the currently rendered component/variant (for audit tooling / e2e). */
136
+ /**
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
+ */
132
140
  renderedKey = computed(() => {
133
- const el = this.rendererService.renderedElement();
134
- if (!el)
135
- return null;
136
- const comp = this.navigationService.activeComponent();
137
- if (!comp)
138
- return null;
139
- return `${comp.meta.className}:${this.rendererService.activeVariantIndex()}`;
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
+ });
140
151
  }, /* @ts-ignore */
141
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 */ []));
160
+ /**
161
+ * The states forced on the canvas, space-separated, `null` for none. Capture
162
+ * mode keys the room it leaves for focus rings on it. The active state's
163
+ * pseudo-classes count only while it fits.
164
+ */
165
+ forcedStates = computed(() => {
166
+ const names = appliedStateNames(this.pseudoMarker.applied());
167
+ return names.length > 0 ? names.join(' ') : null;
168
+ }, /* @ts-ignore */
169
+ ...(ngDevMode ? [{ debugName: "forcedStates" }] : /* istanbul ignore next */ []));
142
170
  /** Resolved canvas layout for the active variant. Variant overrides component config; defaults to 'fit'. */
143
171
  canvasLayout = computed(() => {
144
172
  const comp = this.navigationService.activeComponent();
@@ -183,13 +211,14 @@ export class PrismRendererComponent {
183
211
  return;
184
212
  }
185
213
  performance.mark('prism:rerender:start');
186
- const knownInputs = buildKnownInputs(comp);
214
+ const inputBindings = buildInputBindings(comp);
187
215
  for (const [key, value] of Object.entries(inputs)) {
188
- if (!knownInputs.has(key)) {
216
+ const binding = inputBindings.get(key);
217
+ if (binding === undefined) {
189
218
  console.warn(`[ng-prism] Unknown input "${key}" on <${comp.meta.componentMeta.selector}>, skipping. Remove it from @Showcase variants.`);
190
219
  continue;
191
220
  }
192
- ref.setInput(key, value);
221
+ ref.setInput(binding, value);
193
222
  }
194
223
  ref.changeDetectorRef.detectChanges();
195
224
  performance.mark('prism:rerender:end');
@@ -218,7 +247,21 @@ export class PrismRendererComponent {
218
247
  }
219
248
  });
220
249
  });
221
- this.destroyRef.onDestroy(() => this.cleanup());
250
+ // `renderedKey` waits for what this applies.
251
+ effect(() => {
252
+ const element = this.rendererService.renderedElement();
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);
260
+ });
261
+ this.destroyRef.onDestroy(() => {
262
+ this.cleanup();
263
+ this.pseudoMarker.clear();
264
+ });
222
265
  }
223
266
  createComponent(comp) {
224
267
  this.cleanup();
@@ -250,7 +293,9 @@ export class PrismRendererComponent {
250
293
  });
251
294
  const content = this.rendererService.activeContent();
252
295
  this.lastProjectedContent = content;
253
- 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;
254
299
  this.componentRef = this.outlet().createComponent(comp.type, {
255
300
  injector,
256
301
  projectableNodes
@@ -262,13 +307,14 @@ export class PrismRendererComponent {
262
307
  this.outputSubscriptions.push(sub);
263
308
  }
264
309
  }
265
- const knownInputs = buildKnownInputs(comp);
310
+ const inputBindings = buildInputBindings(comp);
266
311
  for (const [key, value] of Object.entries(this.rendererService.inputValues())) {
267
- if (!knownInputs.has(key)) {
312
+ const binding = inputBindings.get(key);
313
+ if (binding === undefined) {
268
314
  console.warn(`[ng-prism] Unknown input "${key}" on <${selector}>, skipping. Remove it from @Showcase variants.`);
269
315
  continue;
270
316
  }
271
- this.componentRef.setInput(key, value);
317
+ this.componentRef.setInput(binding, value);
272
318
  }
273
319
  this.componentRef.changeDetectorRef.detectChanges();
274
320
  performance.mark('prism:render:end', detail);
@@ -295,7 +341,7 @@ export class PrismRendererComponent {
295
341
  i0.ɵɵviewQuerySignal(ctx.outlet, _c0, 5, ViewContainerRef);
296
342
  } if (rf & 2) {
297
343
  i0.ɵɵqueryAdvance();
298
- } }, decls: 6, vars: 14, 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) {
299
345
  i0.ɵɵelementStart(0, "div", 1);
300
346
  i0.ɵɵconditionalCreate(1, PrismRendererComponent_Conditional_1_Template, 5, 4);
301
347
  i0.ɵɵelementStart(2, "div", 2);
@@ -309,15 +355,15 @@ export class PrismRendererComponent {
309
355
  i0.ɵɵconditional(!ctx.capture.active() ? 1 : -1);
310
356
  i0.ɵɵadvance();
311
357
  i0.ɵɵstyleProp("--%NS%zoom", ctx.canvasService.zoom());
312
- i0.ɵɵattribute("data-prism-rendered", ctx.renderedKey())("data-canvas-layout", ctx.canvasLayout())("data-viewport", ctx.viewportWidth() !== null ? "" : null);
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());
313
359
  i0.ɵɵadvance(3);
314
360
  i0.ɵɵconditional(ctx.activeOverlay() ? 5 : -1);
315
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 }"] });
316
362
  }
317
363
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismRendererComponent, [{
318
364
  type: Component,
319
- 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 <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"] }]
320
366
  }], () => [], { outlet: [{ type: i0.ViewChild, args: ['outlet', { ...{
321
367
  read: ViewContainerRef
322
368
  }, isSignal: true }] }] }); })();
323
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 51 }); })();
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