@compsych-ui-components/angular 7.0.5 → 7.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm2022/index.js CHANGED
@@ -6,6 +6,7 @@ import { HeaderNavComponent } from './lib/header-nav/header-nav';
6
6
  import { SidebarComponent } from './lib/sidebar/sidebar';
7
7
  import { CardComponent } from './lib/card-base/card';
8
8
  import { CardGridComponent } from './lib/card-grid/card-grid';
9
+ import { TextInputComponent } from './lib/text-input/text-input';
9
10
  import * as i0 from "@angular/core";
10
11
  export { ButtonComponent } from './lib/button/button';
11
12
  export { AccordionComponent } from './lib/accordion/accordion';
@@ -15,6 +16,7 @@ export { SidebarComponent } from './lib/sidebar/sidebar';
15
16
  export { CardComponent } from './lib/card-base/card';
16
17
  export { RippleDirective } from './lib/card-base/ripple.directive';
17
18
  export { CardGridComponent } from './lib/card-grid/card-grid';
19
+ export { TextInputComponent } from './lib/text-input/text-input';
18
20
  export class CompsychAngularModule {
19
21
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
20
22
  static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule, imports: [ButtonComponent,
@@ -23,13 +25,15 @@ export class CompsychAngularModule {
23
25
  HeaderNavComponent,
24
26
  SidebarComponent,
25
27
  CardComponent,
26
- CardGridComponent], exports: [ButtonComponent,
28
+ CardGridComponent,
29
+ TextInputComponent], exports: [ButtonComponent,
27
30
  AccordionComponent,
28
31
  FooterComponent,
29
32
  HeaderNavComponent,
30
33
  SidebarComponent,
31
34
  CardComponent,
32
- CardGridComponent] });
35
+ CardGridComponent,
36
+ TextInputComponent] });
33
37
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule });
34
38
  }
35
39
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule, decorators: [{
@@ -43,6 +47,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
43
47
  SidebarComponent,
44
48
  CardComponent,
45
49
  CardGridComponent,
50
+ TextInputComponent,
46
51
  ],
47
52
  exports: [
48
53
  ButtonComponent,
@@ -52,6 +57,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
52
57
  SidebarComponent,
53
58
  CardComponent,
54
59
  CardGridComponent,
60
+ TextInputComponent,
55
61
  ],
56
62
  }]
57
63
  }] });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/angular/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;;AAE9D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAuB9D,MAAM,OAAO,qBAAqB;wGAArB,qBAAqB;yGAArB,qBAAqB,YAlB9B,eAAe;YACf,kBAAkB;YAClB,eAAe;YACf,kBAAkB;YAClB,gBAAgB;YAChB,aAAa;YACb,iBAAiB,aAGjB,eAAe;YACf,kBAAkB;YAClB,eAAe;YACf,kBAAkB;YAClB,gBAAgB;YAChB,aAAa;YACb,iBAAiB;yGAGR,qBAAqB;;4FAArB,qBAAqB;kBApBjC,QAAQ;mBAAC;oBACR,OAAO,EAAE;wBACP,eAAe;wBACf,kBAAkB;wBAClB,eAAe;wBACf,kBAAkB;wBAClB,gBAAgB;wBAChB,aAAa;wBACb,iBAAiB;qBAClB;oBACD,OAAO,EAAE;wBACP,eAAe;wBACf,kBAAkB;wBAClB,eAAe;wBACf,kBAAkB;wBAClB,gBAAgB;wBAChB,aAAa;wBACb,iBAAiB;qBAClB;iBACF","sourcesContent":["import { NgModule } from '@angular/core';\r\nimport { ButtonComponent } from './lib/button/button';\r\nimport { AccordionComponent } from './lib/accordion/accordion';\r\nimport { FooterComponent } from './lib/footer/footer';\r\nimport { HeaderNavComponent } from './lib/header-nav/header-nav';\r\nimport { SidebarComponent } from './lib/sidebar/sidebar';\r\nimport { CardComponent } from './lib/card-base/card';\r\nimport { CardGridComponent } from './lib/card-grid/card-grid';\r\n\r\nexport { ButtonComponent } from './lib/button/button';\r\nexport type { ButtonVariant, ButtonSize, ButtonType } from './lib/button/button';\r\nexport { AccordionComponent } from './lib/accordion/accordion';\r\nexport type { AccordionItem, AccordionButton, AccordionVariant, NestedAccordionItem } from './lib/accordion/accordion-types';\r\nexport { FooterComponent } from './lib/footer/footer';\r\nexport type { FooterLink } from './lib/footer/footer';\r\nexport { HeaderNavComponent } from './lib/header-nav/header-nav';\r\nexport type { HeaderNavLevel, NavAction } from './lib/header-nav/header-nav';\r\nexport { SidebarComponent } from './lib/sidebar/sidebar';\r\nexport type { SidebarItem } from './lib/sidebar/sidebar';\r\nexport { CardComponent } from './lib/card-base/card';\r\nexport type { CardVariant, CardAppearance, CardRadius, CardPadding, CardOrientation } from './lib/card-base/card';\r\nexport { RippleDirective } from './lib/card-base/ripple.directive';\r\nexport { CardGridComponent } from './lib/card-grid/card-grid';\r\nexport type { CardGridLayout } from './lib/card-grid/card-grid';\r\n\r\n@NgModule({\r\n imports: [\r\n ButtonComponent,\r\n AccordionComponent,\r\n FooterComponent,\r\n HeaderNavComponent,\r\n SidebarComponent,\r\n CardComponent,\r\n CardGridComponent,\r\n ],\r\n exports: [\r\n ButtonComponent,\r\n AccordionComponent,\r\n FooterComponent,\r\n HeaderNavComponent,\r\n SidebarComponent,\r\n CardComponent,\r\n CardGridComponent,\r\n ],\r\n})\r\nexport class CompsychAngularModule {}\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/angular/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;;AAEjE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAyBjE,MAAM,OAAO,qBAAqB;wGAArB,qBAAqB;yGAArB,qBAAqB,YApB9B,eAAe;YACf,kBAAkB;YAClB,eAAe;YACf,kBAAkB;YAClB,gBAAgB;YAChB,aAAa;YACb,iBAAiB;YACjB,kBAAkB,aAGlB,eAAe;YACf,kBAAkB;YAClB,eAAe;YACf,kBAAkB;YAClB,gBAAgB;YAChB,aAAa;YACb,iBAAiB;YACjB,kBAAkB;yGAGT,qBAAqB;;4FAArB,qBAAqB;kBAtBjC,QAAQ;mBAAC;oBACR,OAAO,EAAE;wBACP,eAAe;wBACf,kBAAkB;wBAClB,eAAe;wBACf,kBAAkB;wBAClB,gBAAgB;wBAChB,aAAa;wBACb,iBAAiB;wBACjB,kBAAkB;qBACnB;oBACD,OAAO,EAAE;wBACP,eAAe;wBACf,kBAAkB;wBAClB,eAAe;wBACf,kBAAkB;wBAClB,gBAAgB;wBAChB,aAAa;wBACb,iBAAiB;wBACjB,kBAAkB;qBACnB;iBACF","sourcesContent":["import { NgModule } from '@angular/core';\r\nimport { ButtonComponent } from './lib/button/button';\r\nimport { AccordionComponent } from './lib/accordion/accordion';\r\nimport { FooterComponent } from './lib/footer/footer';\r\nimport { HeaderNavComponent } from './lib/header-nav/header-nav';\r\nimport { SidebarComponent } from './lib/sidebar/sidebar';\r\nimport { CardComponent } from './lib/card-base/card';\r\nimport { CardGridComponent } from './lib/card-grid/card-grid';\r\nimport { TextInputComponent } from './lib/text-input/text-input';\r\n\r\nexport { ButtonComponent } from './lib/button/button';\r\nexport type { ButtonVariant, ButtonSize, ButtonType } from './lib/button/button';\r\nexport { AccordionComponent } from './lib/accordion/accordion';\r\nexport type { AccordionItem, AccordionButton, AccordionVariant, NestedAccordionItem } from './lib/accordion/accordion-types';\r\nexport { FooterComponent } from './lib/footer/footer';\r\nexport type { FooterLink } from './lib/footer/footer';\r\nexport { HeaderNavComponent } from './lib/header-nav/header-nav';\r\nexport type { HeaderNavLevel, NavAction } from './lib/header-nav/header-nav';\r\nexport { SidebarComponent } from './lib/sidebar/sidebar';\r\nexport type { SidebarItem } from './lib/sidebar/sidebar';\r\nexport { CardComponent } from './lib/card-base/card';\r\nexport type { CardVariant, CardAppearance, CardRadius, CardPadding, CardOrientation, CardHeadingLevel } from './lib/card-base/card';\r\nexport { RippleDirective } from './lib/card-base/ripple.directive';\r\nexport { CardGridComponent } from './lib/card-grid/card-grid';\r\nexport type { CardGridLayout } from './lib/card-grid/card-grid';\r\nexport { TextInputComponent } from './lib/text-input/text-input';\r\nexport type { TextInputSize, TextInputType } from './lib/text-input/text-input';\r\n\r\n@NgModule({\r\n imports: [\r\n ButtonComponent,\r\n AccordionComponent,\r\n FooterComponent,\r\n HeaderNavComponent,\r\n SidebarComponent,\r\n CardComponent,\r\n CardGridComponent,\r\n TextInputComponent,\r\n ],\r\n exports: [\r\n ButtonComponent,\r\n AccordionComponent,\r\n FooterComponent,\r\n HeaderNavComponent,\r\n SidebarComponent,\r\n CardComponent,\r\n CardGridComponent,\r\n TextInputComponent,\r\n ],\r\n})\r\nexport class CompsychAngularModule {}\r\n"]}
@@ -1,4 +1,5 @@
1
1
  import { ChangeDetectionStrategy, Component, computed, effect, inject, input, output, } from '@angular/core';
2
+ import { NgTemplateOutlet } from '@angular/common';
2
3
  import { DomSanitizer } from '@angular/platform-browser';
3
4
  import { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';
4
5
  import { RippleDirective } from './ripple.directive';
@@ -72,6 +73,15 @@ export class CardComponent {
72
73
  value = input('', ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
73
74
  /** Metric: selected/current state (primary border + text). */
74
75
  current = input(false, ...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
76
+ // ── Accessibility ───────────────────────────────────────
77
+ /**
78
+ * Opts the title / hero headline / action label into a heading in the
79
+ * accessibility tree via `role="heading"` + `aria-level`, so consumers that
80
+ * replace real headings keep heading semantics and screen-reader heading
81
+ * navigation (WCAG 1.3.1). Omit (default) to render as plain text — no visual
82
+ * or behavioral change for existing usages.
83
+ */
84
+ headingLevel = input(undefined, ...(ngDevMode ? [{ debugName: "headingLevel" }] : /* istanbul ignore next */ []));
75
85
  /** Emits on click / Enter / Space when interactive and not disabled. */
76
86
  cardClick = output();
77
87
  sanitizer = inject(DomSanitizer);
@@ -125,11 +135,11 @@ export class CardComponent {
125
135
  return this.sanitizer.bypassSecurityTrustHtml(svg);
126
136
  }
127
137
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
128
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: CardComponent, isStandalone: true, selector: "compsych-card", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, radius: { classPropertyName: "radius", publicName: "radius", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, imageSrc: { classPropertyName: "imageSrc", publicName: "imageSrc", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, plainIcon: { classPropertyName: "plainIcon", publicName: "plainIcon", isSignal: true, isRequired: false, transformFunction: null }, category: { classPropertyName: "category", publicName: "category", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, headline: { classPropertyName: "headline", publicName: "headline", isSignal: true, isRequired: false, transformFunction: null }, actionLabel: { classPropertyName: "actionLabel", publicName: "actionLabel", isSignal: true, isRequired: false, transformFunction: null }, showArrow: { classPropertyName: "showArrow", publicName: "showArrow", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, trailingIcon: { classPropertyName: "trailingIcon", publicName: "trailingIcon", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, current: { classPropertyName: "current", publicName: "current", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cardClick: "cardClick" }, host: { listeners: { "click": "onActivate()", "keydown": "onKeyDown($event)" }, properties: { "class.cmp-card--outlined": "appearance() === 'outlined'", "class.cmp-card--filled": "appearance() === 'filled'", "class.cmp-card--gradient": "appearance() === 'gradient'", "class.cmp-card--image": "appearance() === 'image'", "class.cmp-card--radius-md": "radius() === 'md'", "class.cmp-card--radius-lg": "radius() === 'lg'", "class.cmp-card--radius-xl": "radius() === 'xl'", "class.cmp-card--pad-none": "effectivePadding() === 'none'", "class.cmp-card--pad-sm": "effectivePadding() === 'sm'", "class.cmp-card--pad-md": "effectivePadding() === 'md'", "class.cmp-card--pad-lg": "effectivePadding() === 'lg'", "class.cmp-card--pad-xl": "effectivePadding() === 'xl'", "class.cmp-card--interactive": "interactive()", "class.cmp-card--disabled": "disabled()", "attr.role": "interactive() ? 'button' : null", "attr.tabindex": "interactive() && !disabled() ? 0 : null", "attr.aria-disabled": "disabled() || null", "attr.aria-pressed": "interactive() && variant() === 'metric' ? current() : null", "style.border-color": "variant() === 'metric' && current() ? 'var(--accent-primary-sys-primary)' : null", "style.border-width": "variant() === 'metric' && current() ? 'var(--border-width-sys-stroke-thick)' : null" }, classAttribute: "cmp-card" }, hostDirectives: [{ directive: i1.RippleDirective }], ngImport: i0, template: "@if (isImage() && mediaUrl()) {\r\n <div class=\"cmp-card__media\" [style.background-image]=\"mediaUrl()\"></div>\r\n <div class=\"cmp-card__scrim\" aria-hidden=\"true\"></div>\r\n}\r\n\r\n<div class=\"cmp-card__body\">\r\n @switch (variant()) {\r\n @case ('content') {\r\n @if (headline()) {\r\n <!-- Hero (image/filled): icon top-left, eyebrow + display headline + action bottom. -->\r\n <div class=\"cc cc--hero\" [class.cc--image]=\"isImage()\" [class.cc--filled]=\"appearance() === 'filled'\">\r\n @if (icon()) {\r\n @if (isImage()) {\r\n <div class=\"cc__media-icon\" [innerHTML]=\"iconSvg(icon(), 28)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__img-foot\">\r\n <div class=\"cc__hero-text\">\r\n @if (title()) { <p class=\"cc__eyebrow\">{{ title() }}</p> }\r\n <p class=\"cc__headline\">{{ headline() }}</p>\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else if (isImage()) {\r\n <div class=\"cc cc--image\">\r\n <div class=\"cc__img-top\">\r\n @if (category()) { <span class=\"cc__chip cc__chip--glass\">{{ category() }}</span> }\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <p class=\"cc__title\">{{ title() }}</p> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n <div class=\"cc__img-foot cc__img-foot--end\">\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"cc\" [class.cc--filled]=\"appearance() === 'filled'\" [class.cc--journey]=\"plainIcon()\">\r\n <div class=\"cc__top\">\r\n @if (icon()) {\r\n @if (plainIcon()) {\r\n <div class=\"cc__plain-icon\" [innerHTML]=\"iconSvg(icon(), 48, 1.5)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__top-end\">\r\n @if (category()) { <span class=\"cc__chip\">{{ category() }}</span> }\r\n @if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"cc__text\">\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <p class=\"cc__title\">{{ title() }}</p> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) { <span class=\"cc__button\">{{ actionLabel() }}</span> }\r\n </div>\r\n }\r\n }\r\n\r\n @case ('action') {\r\n @if (orientation() === 'vertical') {\r\n <div class=\"ac ac--vertical\">\r\n @if (icon()) {\r\n <div class=\"ac__badge\" [innerHTML]=\"iconSvg(icon(), 24, 1.5)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <span class=\"ac__label\">{{ label() }}</span>\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"ac\">\r\n @if (icon()) {\r\n <div class=\"ac__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <span class=\"ac__label\">{{ label() }}</span>\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n <span class=\"ac__arrow\" [innerHTML]=\"iconSvg(trailingIcon(), 20)\" aria-hidden=\"true\"></span>\r\n </div>\r\n }\r\n }\r\n\r\n @case ('metric') {\r\n <div class=\"mc\" [class.mc--current]=\"current()\">\r\n <span class=\"mc__value\">{{ value() }}</span>\r\n @if (label()) { <span class=\"mc__label\">{{ label() }}</span> }\r\n </div>\r\n }\r\n\r\n @default {\r\n <ng-content />\r\n }\r\n }\r\n</div>\r\n", styles: [":host{position:relative;display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden;border-radius:var(--border-radius-sys-radius-lg);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);isolation:isolate}:host.cmp-card--radius-md{border-radius:var(--border-radius-sys-radius-md)}:host.cmp-card--radius-lg{border-radius:var(--border-radius-sys-radius-lg)}:host.cmp-card--radius-xl{border-radius:var(--border-radius-sys-radius-xl)}.cmp-card__body{position:relative;z-index:1;display:flex;flex-direction:column;flex:1 1 auto;min-height:0}:host.cmp-card--pad-none .cmp-card__body{padding:0}:host.cmp-card--pad-sm .cmp-card__body{padding:var(--spacing-padding-sys-padding-16)}:host.cmp-card--pad-md .cmp-card__body{padding:var(--spacing-padding-sys-padding-20)}:host.cmp-card--pad-lg .cmp-card__body{padding:var(--spacing-padding-sys-padding-24)}:host.cmp-card--pad-xl .cmp-card__body{padding:var(--spacing-padding-sys-padding-32)}:host.cmp-card--outlined{background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host.cmp-card--filled{background-color:var(--accent-primary-sys-primary-container);color:var(--accent-primary-sys-on-primary-container)}:host.cmp-card--gradient{background-image:linear-gradient(180deg,var(--transparent-primary-sys-primary-08) 0%,var(--transparent-neutral-sys-white-10) 100%);background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host.cmp-card--image{color:var(--accent-primary-sys-on-primary-container);background-color:var(--surface-surface-container-sys-surface-container-high)}.cmp-card__media{position:absolute;inset:0;z-index:0;background-size:cover;background-position:center}.cmp-card__scrim{position:absolute;inset:0;z-index:0;background-image:linear-gradient(180deg,var(--transparent-neutral-sys-black-40) 0%,var(--transparent-neutral-sys-black-10) 32%,var(--transparent-neutral-sys-black-10) 64%,var(--transparent-neutral-sys-black-50) 100%);mix-blend-mode:multiply}:host.cmp-card--interactive{cursor:pointer;transition:box-shadow .14s ease-out,background-color .14s ease-out,border-color .14s ease-out}:host.cmp-card--interactive:focus-visible{outline:none;border-color:var(--add-on-primary-fixed-sys-on-primary-fixed-variant);box-shadow:0 0 0 var(--spacing-padding-sys-padding-4) var(--transparent-primary-sys-primary-08)}:host.cmp-card--interactive.cmp-card--outlined:hover,:host.cmp-card--interactive.cmp-card--gradient:hover{background-color:var(--surface-surface-container-sys-surface-container);box-shadow:0 .25rem 1rem #0000001a,0 .125rem .25rem #00000014}:host.cmp-card--interactive.cmp-card--filled:hover,:host.cmp-card--interactive.cmp-card--image:hover{box-shadow:0 .25rem 1rem #0000002e,0 .125rem .25rem #0000001f}:host.cmp-card--disabled{pointer-events:none;opacity:.38}@media(prefers-reduced-motion:reduce){:host.cmp-card--interactive{transition:none}}@media(forced-colors:active){:host{border:1px solid CanvasText;forced-color-adjust:none}:host.cmp-card--interactive:focus-visible{outline:2px solid Highlight;outline-offset:2px}}.cc{display:flex;flex-direction:column;flex:1 1 auto;gap:var(--spacing-padding-sys-padding-24);font-family:var(--body-medium-sys-font-family)}.cc__top{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--spacing-padding-sys-padding-8)}.cc__icon{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary);line-height:0}.cc__plain-icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface);line-height:0}.cc--journey{justify-content:space-between}.cc--journey .cc__title{font-weight:var(--title-small-sys-font-weight)}.cc__top-end{display:inline-flex;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.cc__arrow{display:inline-flex;color:var(--surface-surface-sys-on-surface-variant);flex-shrink:0;line-height:0}.cc__text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-12)}.cc__heading{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4)}.cc__title{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight-emphasized);letter-spacing:var(--title-small-sys-tracking);color:var(--surface-surface-sys-on-surface)}.cc__subtitle{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight-emphasized);letter-spacing:var(--title-small-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.cc__desc{margin:0;font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height);letter-spacing:var(--body-medium-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.cc__chip{display:inline-flex;align-items:center;padding:var(--spacing-padding-sys-padding-2) var(--spacing-padding-sys-padding-12);border-radius:var(--border-radius-sys-radius-full);background-color:var(--custom-info-sys-info-container);color:var(--custom-info-sys-on-info-container);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);line-height:var(--label-medium-sys-line-height);white-space:nowrap}.cc__chip--glass{background-color:var(--transparent-neutral-sys-white-20);color:var(--accent-primary-sys-on-primary);-webkit-backdrop-filter:blur(.375rem);backdrop-filter:blur(.375rem)}.cc__button{align-self:flex-start;display:inline-flex;align-items:center;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-32);border-radius:var(--border-radius-sys-radius-full);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);white-space:nowrap}.cc--filled .cc__icon{background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--accent-primary-sys-primary)}.cc--filled .cc__title{color:var(--accent-primary-sys-on-primary-container)}.cc--filled .cc__subtitle,.cc--filled .cc__desc,.cc--filled .cc__arrow{color:var(--accent-primary-sys-on-primary-container-variant)}.cc--filled .cc__button{border-color:transparent}.cc--image{position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-height:12.5rem;justify-content:space-between;gap:var(--spacing-padding-sys-padding-24);padding:var(--spacing-padding-sys-padding-24)}.cc--hero{position:relative;display:flex;flex-direction:column;flex:1 1 auto;justify-content:space-between;gap:var(--spacing-padding-sys-padding-24);min-height:22.5rem;padding:var(--spacing-padding-sys-padding-32)}.cc__media-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--transparent-neutral-sys-white-20);color:var(--accent-primary-sys-on-primary);-webkit-backdrop-filter:blur(.375rem);backdrop-filter:blur(.375rem);line-height:0}.cc__img-top{display:flex;flex-direction:column;align-items:flex-start;gap:var(--spacing-padding-sys-padding-12)}.cc__img-foot{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--spacing-padding-sys-padding-16)}.cc__img-foot--end{justify-content:flex-end}.cc__hero-text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-8);min-width:0}.cc__eyebrow{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);color:var(--accent-primary-sys-on-primary)}.cc__headline{margin:0;font-family:var(--headline-large-sys-font-family);font-size:var(--headline-large-sys-font-size);line-height:var(--headline-large-sys-line-height);font-weight:var(--headline-large-sys-font-weight-emphasized);letter-spacing:var(--headline-large-sys-tracking);color:var(--accent-primary-sys-on-primary)}.cc__button--solid{align-self:flex-end;flex-shrink:0;border-color:transparent;box-shadow:0 .125rem .25rem #0000000f,0 .125rem .5rem #0000000a}.cc--image .cc__title,.cc--image .cc__arrow{color:var(--accent-primary-sys-on-primary)}.cc--image .cc__subtitle,.cc--image .cc__desc{color:var(--transparent-neutral-sys-white-80)}.ac{display:flex;flex:1 1 auto;align-items:center;gap:var(--spacing-padding-sys-padding-12);font-family:var(--body-medium-sys-font-family)}.ac__icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface);line-height:0}.ac__text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4);flex:1 1 auto;min-width:0}.ac__label{font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);line-height:var(--label-large-sys-line-height);font-weight:var(--label-large-sys-font-weight-emphasized);letter-spacing:var(--label-large-sys-tracking);color:var(--surface-surface-sys-on-surface);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ac__desc{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height);letter-spacing:var(--body-small-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.ac__arrow{display:inline-flex;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant);line-height:0}.ac--vertical{flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:var(--spacing-padding-sys-padding-16)}.ac__badge{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--surface-surface-container-sys-surface-container);color:var(--surface-surface-sys-on-surface);transition:background-color .14s ease-out;line-height:0}:host(:hover) .ac__badge{background-color:var(--surface-surface-container-sys-surface-container-lowest)}.ac--vertical .ac__label{white-space:pre-line;overflow:visible;font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight)}@media(prefers-reduced-motion:reduce){.ac__badge{transition:none}}.mc{display:flex;flex:1 1 auto;flex-direction:column;align-items:center;justify-content:center;gap:var(--spacing-padding-sys-padding-4);padding:var(--spacing-padding-sys-padding-8) 0;text-align:center;font-family:var(--body-medium-sys-font-family)}.mc__value{font-family:var(--headline-small-sys-font-family);font-size:var(--headline-small-sys-font-size);line-height:var(--headline-small-sys-line-height);font-weight:var(--headline-small-sys-font-weight-emphasized);letter-spacing:var(--headline-small-sys-tracking);color:var(--surface-surface-sys-on-surface)}.mc__label{font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.mc--current .mc__value,.mc--current .mc__label{color:var(--accent-primary-sys-primary)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
138
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: CardComponent, isStandalone: true, selector: "compsych-card", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null }, radius: { classPropertyName: "radius", publicName: "radius", isSignal: true, isRequired: false, transformFunction: null }, padding: { classPropertyName: "padding", publicName: "padding", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, imageSrc: { classPropertyName: "imageSrc", publicName: "imageSrc", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, plainIcon: { classPropertyName: "plainIcon", publicName: "plainIcon", isSignal: true, isRequired: false, transformFunction: null }, category: { classPropertyName: "category", publicName: "category", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, headline: { classPropertyName: "headline", publicName: "headline", isSignal: true, isRequired: false, transformFunction: null }, actionLabel: { classPropertyName: "actionLabel", publicName: "actionLabel", isSignal: true, isRequired: false, transformFunction: null }, showArrow: { classPropertyName: "showArrow", publicName: "showArrow", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, trailingIcon: { classPropertyName: "trailingIcon", publicName: "trailingIcon", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, current: { classPropertyName: "current", publicName: "current", isSignal: true, isRequired: false, transformFunction: null }, headingLevel: { classPropertyName: "headingLevel", publicName: "headingLevel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cardClick: "cardClick" }, host: { listeners: { "click": "onActivate()", "keydown": "onKeyDown($event)" }, properties: { "class.cmp-card--outlined": "appearance() === 'outlined'", "class.cmp-card--filled": "appearance() === 'filled'", "class.cmp-card--gradient": "appearance() === 'gradient'", "class.cmp-card--image": "appearance() === 'image'", "class.cmp-card--radius-md": "radius() === 'md'", "class.cmp-card--radius-lg": "radius() === 'lg'", "class.cmp-card--radius-xl": "radius() === 'xl'", "class.cmp-card--pad-none": "effectivePadding() === 'none'", "class.cmp-card--pad-sm": "effectivePadding() === 'sm'", "class.cmp-card--pad-md": "effectivePadding() === 'md'", "class.cmp-card--pad-lg": "effectivePadding() === 'lg'", "class.cmp-card--pad-xl": "effectivePadding() === 'xl'", "class.cmp-card--interactive": "interactive()", "class.cmp-card--disabled": "disabled()", "attr.role": "interactive() ? 'button' : null", "attr.tabindex": "interactive() && !disabled() ? 0 : null", "attr.aria-disabled": "disabled() || null", "attr.aria-pressed": "interactive() && variant() === 'metric' ? current() : null", "style.border-color": "variant() === 'metric' && current() ? 'var(--accent-primary-sys-primary)' : null", "style.border-width": "variant() === 'metric' && current() ? 'var(--border-width-sys-stroke-thick)' : null" }, classAttribute: "cmp-card" }, hostDirectives: [{ directive: i1.RippleDirective }], ngImport: i0, template: "@if (isImage() && mediaUrl()) {\r\n <div class=\"cmp-card__media\" [style.background-image]=\"mediaUrl()\"></div>\r\n <div class=\"cmp-card__scrim\" aria-hidden=\"true\"></div>\r\n}\r\n\r\n<div class=\"cmp-card__body\">\r\n @switch (variant()) {\r\n @case ('content') {\r\n @if (headline()) {\r\n <!-- Hero (image/filled): icon top-left, eyebrow + display headline + action bottom. -->\r\n <div class=\"cc cc--hero\" [class.cc--image]=\"isImage()\" [class.cc--filled]=\"appearance() === 'filled'\">\r\n @if (icon()) {\r\n @if (isImage()) {\r\n <div class=\"cc__media-icon\" [innerHTML]=\"iconSvg(icon(), 28)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__img-foot\">\r\n <div class=\"cc__hero-text\">\r\n @if (title()) { <p class=\"cc__eyebrow\">{{ title() }}</p> }\r\n <ng-container [ngTemplateOutlet]=\"headlineTpl\" />\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else if (isImage()) {\r\n <div class=\"cc cc--image\">\r\n <div class=\"cc__img-top\">\r\n @if (category()) { <span class=\"cc__chip cc__chip--glass\">{{ category() }}</span> }\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <ng-container [ngTemplateOutlet]=\"titleTpl\" /> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n <div class=\"cc__img-foot cc__img-foot--end\">\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"cc\" [class.cc--filled]=\"appearance() === 'filled'\" [class.cc--journey]=\"plainIcon()\">\r\n <div class=\"cc__top\">\r\n @if (icon()) {\r\n @if (plainIcon()) {\r\n <div class=\"cc__plain-icon\" [innerHTML]=\"iconSvg(icon(), 48, 1.5)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__top-end\">\r\n @if (category()) { <span class=\"cc__chip\">{{ category() }}</span> }\r\n @if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"cc__text\">\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <ng-container [ngTemplateOutlet]=\"titleTpl\" /> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) { <span class=\"cc__button\">{{ actionLabel() }}</span> }\r\n </div>\r\n }\r\n }\r\n\r\n @case ('action') {\r\n @if (orientation() === 'vertical') {\r\n <div class=\"ac ac--vertical\">\r\n @if (icon()) {\r\n <div class=\"ac__badge\" [innerHTML]=\"iconSvg(icon(), 24, 1.5)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <ng-container [ngTemplateOutlet]=\"labelTpl\" />\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"ac\">\r\n @if (icon()) {\r\n <div class=\"ac__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <ng-container [ngTemplateOutlet]=\"labelTpl\" />\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n <span class=\"ac__arrow\" [innerHTML]=\"iconSvg(trailingIcon(), 20)\" aria-hidden=\"true\"></span>\r\n </div>\r\n }\r\n }\r\n\r\n @case ('metric') {\r\n <div class=\"mc\" [class.mc--current]=\"current()\">\r\n <span class=\"mc__value\">{{ value() }}</span>\r\n @if (label()) { <span class=\"mc__label\">{{ label() }}</span> }\r\n </div>\r\n }\r\n\r\n @default {\r\n <ng-content />\r\n }\r\n }\r\n</div>\r\n\r\n<!-- Heading fragments: render a real <h2>\u2013<h6> when `headingLevel` is set, else the\r\n original <p>/<span>. Shared across layouts so semantics stay consistent and the\r\n cc__title / cc__headline / ac__label styling is unchanged. -->\r\n<ng-template #titleTpl>\r\n @switch (headingLevel()) {\r\n @case (2) { <h2 class=\"cc__title\">{{ title() }}</h2> }\r\n @case (3) { <h3 class=\"cc__title\">{{ title() }}</h3> }\r\n @case (4) { <h4 class=\"cc__title\">{{ title() }}</h4> }\r\n @case (5) { <h5 class=\"cc__title\">{{ title() }}</h5> }\r\n @case (6) { <h6 class=\"cc__title\">{{ title() }}</h6> }\r\n @default { <p class=\"cc__title\">{{ title() }}</p> }\r\n }\r\n</ng-template>\r\n\r\n<ng-template #headlineTpl>\r\n @switch (headingLevel()) {\r\n @case (2) { <h2 class=\"cc__headline\">{{ headline() }}</h2> }\r\n @case (3) { <h3 class=\"cc__headline\">{{ headline() }}</h3> }\r\n @case (4) { <h4 class=\"cc__headline\">{{ headline() }}</h4> }\r\n @case (5) { <h5 class=\"cc__headline\">{{ headline() }}</h5> }\r\n @case (6) { <h6 class=\"cc__headline\">{{ headline() }}</h6> }\r\n @default { <p class=\"cc__headline\">{{ headline() }}</p> }\r\n }\r\n</ng-template>\r\n\r\n<ng-template #labelTpl>\r\n @switch (headingLevel()) {\r\n @case (2) { <h2 class=\"ac__label\">{{ label() }}</h2> }\r\n @case (3) { <h3 class=\"ac__label\">{{ label() }}</h3> }\r\n @case (4) { <h4 class=\"ac__label\">{{ label() }}</h4> }\r\n @case (5) { <h5 class=\"ac__label\">{{ label() }}</h5> }\r\n @case (6) { <h6 class=\"ac__label\">{{ label() }}</h6> }\r\n @default { <span class=\"ac__label\">{{ label() }}</span> }\r\n }\r\n</ng-template>\r\n", styles: [":host{position:relative;display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden;border-radius:var(--border-radius-sys-radius-lg);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);isolation:isolate}:host.cmp-card--radius-md{border-radius:var(--border-radius-sys-radius-md)}:host.cmp-card--radius-lg{border-radius:var(--border-radius-sys-radius-lg)}:host.cmp-card--radius-xl{border-radius:var(--border-radius-sys-radius-xl)}.cmp-card__body{position:relative;z-index:1;display:flex;flex-direction:column;flex:1 1 auto;min-height:0}:host.cmp-card--pad-none .cmp-card__body{padding:0}:host.cmp-card--pad-sm .cmp-card__body{padding:var(--spacing-padding-sys-padding-16)}:host.cmp-card--pad-md .cmp-card__body{padding:var(--spacing-padding-sys-padding-20)}:host.cmp-card--pad-lg .cmp-card__body{padding:var(--spacing-padding-sys-padding-24)}:host.cmp-card--pad-xl .cmp-card__body{padding:var(--spacing-padding-sys-padding-32)}:host.cmp-card--outlined{background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host.cmp-card--filled{background-color:var(--accent-primary-sys-primary-container);color:var(--accent-primary-sys-on-primary-container)}:host.cmp-card--gradient{background-image:linear-gradient(180deg,var(--transparent-primary-sys-primary-08) 0%,var(--transparent-neutral-sys-white-10) 100%);background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host.cmp-card--image{color:var(--accent-primary-sys-on-primary-container);background-color:var(--surface-surface-container-sys-surface-container-high)}.cmp-card__media{position:absolute;inset:0;z-index:0;background-size:cover;background-position:center}.cmp-card__scrim{position:absolute;inset:0;z-index:0;background-image:linear-gradient(180deg,var(--transparent-neutral-sys-black-40) 0%,var(--transparent-neutral-sys-black-10) 32%,var(--transparent-neutral-sys-black-10) 64%,var(--transparent-neutral-sys-black-50) 100%);mix-blend-mode:multiply}:host.cmp-card--interactive{cursor:pointer;transition:box-shadow .14s ease-out,background-color .14s ease-out,border-color .14s ease-out}:host.cmp-card--interactive:focus-visible{outline:none;border-color:var(--add-on-primary-fixed-sys-on-primary-fixed-variant);box-shadow:0 0 0 var(--spacing-padding-sys-padding-4) var(--transparent-primary-sys-primary-08)}:host.cmp-card--interactive.cmp-card--outlined:hover,:host.cmp-card--interactive.cmp-card--gradient:hover{background-color:var(--surface-surface-container-sys-surface-container);box-shadow:0 .25rem 1rem #0000001a,0 .125rem .25rem #00000014}:host.cmp-card--interactive.cmp-card--filled:hover,:host.cmp-card--interactive.cmp-card--image:hover{box-shadow:0 .25rem 1rem #0000002e,0 .125rem .25rem #0000001f}:host.cmp-card--disabled{pointer-events:none;opacity:.38}@media(prefers-reduced-motion:reduce){:host.cmp-card--interactive{transition:none}}@media(forced-colors:active){:host{border:1px solid CanvasText;forced-color-adjust:none}:host.cmp-card--interactive:focus-visible{outline:2px solid Highlight;outline-offset:2px}}.cc{display:flex;flex-direction:column;flex:1 1 auto;gap:var(--spacing-padding-sys-padding-24);font-family:var(--body-medium-sys-font-family)}.cc__top{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--spacing-padding-sys-padding-8)}.cc__icon{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary);line-height:0}.cc__plain-icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface);line-height:0}.cc--journey{justify-content:space-between}.cc--journey .cc__title{font-weight:var(--title-small-sys-font-weight)}.cc__top-end{display:inline-flex;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.cc__arrow{display:inline-flex;color:var(--surface-surface-sys-on-surface-variant);flex-shrink:0;line-height:0}.cc__text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-12)}.cc__heading{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4)}.cc__title{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight-emphasized);letter-spacing:var(--title-small-sys-tracking);color:var(--surface-surface-sys-on-surface)}.cc__subtitle{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight-emphasized);letter-spacing:var(--title-small-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.cc__desc{margin:0;font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height);letter-spacing:var(--body-medium-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.cc__chip{display:inline-flex;align-items:center;padding:var(--spacing-padding-sys-padding-2) var(--spacing-padding-sys-padding-12);border-radius:var(--border-radius-sys-radius-full);background-color:var(--custom-info-sys-info-container);color:var(--custom-info-sys-on-info-container);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);line-height:var(--label-medium-sys-line-height);white-space:nowrap}.cc__chip--glass{background-color:var(--transparent-neutral-sys-white-20);color:var(--accent-primary-sys-on-primary);-webkit-backdrop-filter:blur(.375rem);backdrop-filter:blur(.375rem)}.cc__button{align-self:flex-start;display:inline-flex;align-items:center;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-32);border-radius:var(--border-radius-sys-radius-full);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);white-space:nowrap}.cc--filled .cc__icon{background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--accent-primary-sys-primary)}.cc--filled .cc__title{color:var(--accent-primary-sys-on-primary-container)}.cc--filled .cc__subtitle,.cc--filled .cc__desc,.cc--filled .cc__arrow{color:var(--accent-primary-sys-on-primary-container-variant)}.cc--filled .cc__button{border-color:transparent}.cc--image{position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-height:12.5rem;justify-content:space-between;gap:var(--spacing-padding-sys-padding-24);padding:var(--spacing-padding-sys-padding-24)}.cc--hero{position:relative;display:flex;flex-direction:column;flex:1 1 auto;justify-content:space-between;gap:var(--spacing-padding-sys-padding-24);min-height:22.5rem;padding:var(--spacing-padding-sys-padding-32)}.cc__media-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--transparent-neutral-sys-white-20);color:var(--accent-primary-sys-on-primary);-webkit-backdrop-filter:blur(.375rem);backdrop-filter:blur(.375rem);line-height:0}.cc__img-top{display:flex;flex-direction:column;align-items:flex-start;gap:var(--spacing-padding-sys-padding-12)}.cc__img-foot{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--spacing-padding-sys-padding-16)}.cc__img-foot--end{justify-content:flex-end}.cc__hero-text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-8);min-width:0}.cc__eyebrow{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);color:var(--accent-primary-sys-on-primary)}.cc__headline{margin:0;font-family:var(--headline-large-sys-font-family);font-size:var(--headline-large-sys-font-size);line-height:var(--headline-large-sys-line-height);font-weight:var(--headline-large-sys-font-weight-emphasized);letter-spacing:var(--headline-large-sys-tracking);color:var(--accent-primary-sys-on-primary)}.cc__button--solid{align-self:flex-end;flex-shrink:0;border-color:transparent;box-shadow:0 .125rem .25rem #0000000f,0 .125rem .5rem #0000000a}.cc--image .cc__title,.cc--image .cc__arrow{color:var(--accent-primary-sys-on-primary)}.cc--image .cc__subtitle,.cc--image .cc__desc{color:var(--transparent-neutral-sys-white-80)}.ac{display:flex;flex:1 1 auto;align-items:center;gap:var(--spacing-padding-sys-padding-12);font-family:var(--body-medium-sys-font-family)}.ac__icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface);line-height:0}.ac__text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4);flex:1 1 auto;min-width:0}.ac__label{margin:0;font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);line-height:var(--label-large-sys-line-height);font-weight:var(--label-large-sys-font-weight-emphasized);letter-spacing:var(--label-large-sys-tracking);color:var(--surface-surface-sys-on-surface);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ac__desc{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height);letter-spacing:var(--body-small-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.ac__arrow{display:inline-flex;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant);line-height:0}.ac--vertical{flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:var(--spacing-padding-sys-padding-16)}.ac__badge{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--surface-surface-container-sys-surface-container);color:var(--surface-surface-sys-on-surface);transition:background-color .14s ease-out;line-height:0}:host(:hover) .ac__badge{background-color:var(--surface-surface-container-sys-surface-container-lowest)}.ac--vertical .ac__label{white-space:pre-line;overflow:visible;font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight)}@media(prefers-reduced-motion:reduce){.ac__badge{transition:none}}.mc{display:flex;flex:1 1 auto;flex-direction:column;align-items:center;justify-content:center;gap:var(--spacing-padding-sys-padding-4);padding:var(--spacing-padding-sys-padding-8) 0;text-align:center;font-family:var(--body-medium-sys-font-family)}.mc__value{font-family:var(--headline-small-sys-font-family);font-size:var(--headline-small-sys-font-size);line-height:var(--headline-small-sys-line-height);font-weight:var(--headline-small-sys-font-weight-emphasized);letter-spacing:var(--headline-small-sys-tracking);color:var(--surface-surface-sys-on-surface)}.mc__label{font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.mc--current .mc__value,.mc--current .mc__label{color:var(--accent-primary-sys-primary)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
129
139
  }
130
140
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CardComponent, decorators: [{
131
141
  type: Component,
132
- args: [{ selector: 'compsych-card', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, hostDirectives: [RippleDirective], host: {
142
+ args: [{ selector: 'compsych-card', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgTemplateOutlet], hostDirectives: [RippleDirective], host: {
133
143
  class: 'cmp-card',
134
144
  '[class.cmp-card--outlined]': "appearance() === 'outlined'",
135
145
  '[class.cmp-card--filled]': "appearance() === 'filled'",
@@ -153,6 +163,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
153
163
  '[style.border-width]': "variant() === 'metric' && current() ? 'var(--border-width-sys-stroke-thick)' : null",
154
164
  '(click)': 'onActivate()',
155
165
  '(keydown)': 'onKeyDown($event)',
156
- }, template: "@if (isImage() && mediaUrl()) {\r\n <div class=\"cmp-card__media\" [style.background-image]=\"mediaUrl()\"></div>\r\n <div class=\"cmp-card__scrim\" aria-hidden=\"true\"></div>\r\n}\r\n\r\n<div class=\"cmp-card__body\">\r\n @switch (variant()) {\r\n @case ('content') {\r\n @if (headline()) {\r\n <!-- Hero (image/filled): icon top-left, eyebrow + display headline + action bottom. -->\r\n <div class=\"cc cc--hero\" [class.cc--image]=\"isImage()\" [class.cc--filled]=\"appearance() === 'filled'\">\r\n @if (icon()) {\r\n @if (isImage()) {\r\n <div class=\"cc__media-icon\" [innerHTML]=\"iconSvg(icon(), 28)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__img-foot\">\r\n <div class=\"cc__hero-text\">\r\n @if (title()) { <p class=\"cc__eyebrow\">{{ title() }}</p> }\r\n <p class=\"cc__headline\">{{ headline() }}</p>\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else if (isImage()) {\r\n <div class=\"cc cc--image\">\r\n <div class=\"cc__img-top\">\r\n @if (category()) { <span class=\"cc__chip cc__chip--glass\">{{ category() }}</span> }\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <p class=\"cc__title\">{{ title() }}</p> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n <div class=\"cc__img-foot cc__img-foot--end\">\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"cc\" [class.cc--filled]=\"appearance() === 'filled'\" [class.cc--journey]=\"plainIcon()\">\r\n <div class=\"cc__top\">\r\n @if (icon()) {\r\n @if (plainIcon()) {\r\n <div class=\"cc__plain-icon\" [innerHTML]=\"iconSvg(icon(), 48, 1.5)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__top-end\">\r\n @if (category()) { <span class=\"cc__chip\">{{ category() }}</span> }\r\n @if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"cc__text\">\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <p class=\"cc__title\">{{ title() }}</p> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) { <span class=\"cc__button\">{{ actionLabel() }}</span> }\r\n </div>\r\n }\r\n }\r\n\r\n @case ('action') {\r\n @if (orientation() === 'vertical') {\r\n <div class=\"ac ac--vertical\">\r\n @if (icon()) {\r\n <div class=\"ac__badge\" [innerHTML]=\"iconSvg(icon(), 24, 1.5)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <span class=\"ac__label\">{{ label() }}</span>\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"ac\">\r\n @if (icon()) {\r\n <div class=\"ac__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <span class=\"ac__label\">{{ label() }}</span>\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n <span class=\"ac__arrow\" [innerHTML]=\"iconSvg(trailingIcon(), 20)\" aria-hidden=\"true\"></span>\r\n </div>\r\n }\r\n }\r\n\r\n @case ('metric') {\r\n <div class=\"mc\" [class.mc--current]=\"current()\">\r\n <span class=\"mc__value\">{{ value() }}</span>\r\n @if (label()) { <span class=\"mc__label\">{{ label() }}</span> }\r\n </div>\r\n }\r\n\r\n @default {\r\n <ng-content />\r\n }\r\n }\r\n</div>\r\n", styles: [":host{position:relative;display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden;border-radius:var(--border-radius-sys-radius-lg);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);isolation:isolate}:host.cmp-card--radius-md{border-radius:var(--border-radius-sys-radius-md)}:host.cmp-card--radius-lg{border-radius:var(--border-radius-sys-radius-lg)}:host.cmp-card--radius-xl{border-radius:var(--border-radius-sys-radius-xl)}.cmp-card__body{position:relative;z-index:1;display:flex;flex-direction:column;flex:1 1 auto;min-height:0}:host.cmp-card--pad-none .cmp-card__body{padding:0}:host.cmp-card--pad-sm .cmp-card__body{padding:var(--spacing-padding-sys-padding-16)}:host.cmp-card--pad-md .cmp-card__body{padding:var(--spacing-padding-sys-padding-20)}:host.cmp-card--pad-lg .cmp-card__body{padding:var(--spacing-padding-sys-padding-24)}:host.cmp-card--pad-xl .cmp-card__body{padding:var(--spacing-padding-sys-padding-32)}:host.cmp-card--outlined{background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host.cmp-card--filled{background-color:var(--accent-primary-sys-primary-container);color:var(--accent-primary-sys-on-primary-container)}:host.cmp-card--gradient{background-image:linear-gradient(180deg,var(--transparent-primary-sys-primary-08) 0%,var(--transparent-neutral-sys-white-10) 100%);background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host.cmp-card--image{color:var(--accent-primary-sys-on-primary-container);background-color:var(--surface-surface-container-sys-surface-container-high)}.cmp-card__media{position:absolute;inset:0;z-index:0;background-size:cover;background-position:center}.cmp-card__scrim{position:absolute;inset:0;z-index:0;background-image:linear-gradient(180deg,var(--transparent-neutral-sys-black-40) 0%,var(--transparent-neutral-sys-black-10) 32%,var(--transparent-neutral-sys-black-10) 64%,var(--transparent-neutral-sys-black-50) 100%);mix-blend-mode:multiply}:host.cmp-card--interactive{cursor:pointer;transition:box-shadow .14s ease-out,background-color .14s ease-out,border-color .14s ease-out}:host.cmp-card--interactive:focus-visible{outline:none;border-color:var(--add-on-primary-fixed-sys-on-primary-fixed-variant);box-shadow:0 0 0 var(--spacing-padding-sys-padding-4) var(--transparent-primary-sys-primary-08)}:host.cmp-card--interactive.cmp-card--outlined:hover,:host.cmp-card--interactive.cmp-card--gradient:hover{background-color:var(--surface-surface-container-sys-surface-container);box-shadow:0 .25rem 1rem #0000001a,0 .125rem .25rem #00000014}:host.cmp-card--interactive.cmp-card--filled:hover,:host.cmp-card--interactive.cmp-card--image:hover{box-shadow:0 .25rem 1rem #0000002e,0 .125rem .25rem #0000001f}:host.cmp-card--disabled{pointer-events:none;opacity:.38}@media(prefers-reduced-motion:reduce){:host.cmp-card--interactive{transition:none}}@media(forced-colors:active){:host{border:1px solid CanvasText;forced-color-adjust:none}:host.cmp-card--interactive:focus-visible{outline:2px solid Highlight;outline-offset:2px}}.cc{display:flex;flex-direction:column;flex:1 1 auto;gap:var(--spacing-padding-sys-padding-24);font-family:var(--body-medium-sys-font-family)}.cc__top{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--spacing-padding-sys-padding-8)}.cc__icon{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary);line-height:0}.cc__plain-icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface);line-height:0}.cc--journey{justify-content:space-between}.cc--journey .cc__title{font-weight:var(--title-small-sys-font-weight)}.cc__top-end{display:inline-flex;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.cc__arrow{display:inline-flex;color:var(--surface-surface-sys-on-surface-variant);flex-shrink:0;line-height:0}.cc__text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-12)}.cc__heading{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4)}.cc__title{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight-emphasized);letter-spacing:var(--title-small-sys-tracking);color:var(--surface-surface-sys-on-surface)}.cc__subtitle{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight-emphasized);letter-spacing:var(--title-small-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.cc__desc{margin:0;font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height);letter-spacing:var(--body-medium-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.cc__chip{display:inline-flex;align-items:center;padding:var(--spacing-padding-sys-padding-2) var(--spacing-padding-sys-padding-12);border-radius:var(--border-radius-sys-radius-full);background-color:var(--custom-info-sys-info-container);color:var(--custom-info-sys-on-info-container);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);line-height:var(--label-medium-sys-line-height);white-space:nowrap}.cc__chip--glass{background-color:var(--transparent-neutral-sys-white-20);color:var(--accent-primary-sys-on-primary);-webkit-backdrop-filter:blur(.375rem);backdrop-filter:blur(.375rem)}.cc__button{align-self:flex-start;display:inline-flex;align-items:center;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-32);border-radius:var(--border-radius-sys-radius-full);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);white-space:nowrap}.cc--filled .cc__icon{background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--accent-primary-sys-primary)}.cc--filled .cc__title{color:var(--accent-primary-sys-on-primary-container)}.cc--filled .cc__subtitle,.cc--filled .cc__desc,.cc--filled .cc__arrow{color:var(--accent-primary-sys-on-primary-container-variant)}.cc--filled .cc__button{border-color:transparent}.cc--image{position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-height:12.5rem;justify-content:space-between;gap:var(--spacing-padding-sys-padding-24);padding:var(--spacing-padding-sys-padding-24)}.cc--hero{position:relative;display:flex;flex-direction:column;flex:1 1 auto;justify-content:space-between;gap:var(--spacing-padding-sys-padding-24);min-height:22.5rem;padding:var(--spacing-padding-sys-padding-32)}.cc__media-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--transparent-neutral-sys-white-20);color:var(--accent-primary-sys-on-primary);-webkit-backdrop-filter:blur(.375rem);backdrop-filter:blur(.375rem);line-height:0}.cc__img-top{display:flex;flex-direction:column;align-items:flex-start;gap:var(--spacing-padding-sys-padding-12)}.cc__img-foot{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--spacing-padding-sys-padding-16)}.cc__img-foot--end{justify-content:flex-end}.cc__hero-text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-8);min-width:0}.cc__eyebrow{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);color:var(--accent-primary-sys-on-primary)}.cc__headline{margin:0;font-family:var(--headline-large-sys-font-family);font-size:var(--headline-large-sys-font-size);line-height:var(--headline-large-sys-line-height);font-weight:var(--headline-large-sys-font-weight-emphasized);letter-spacing:var(--headline-large-sys-tracking);color:var(--accent-primary-sys-on-primary)}.cc__button--solid{align-self:flex-end;flex-shrink:0;border-color:transparent;box-shadow:0 .125rem .25rem #0000000f,0 .125rem .5rem #0000000a}.cc--image .cc__title,.cc--image .cc__arrow{color:var(--accent-primary-sys-on-primary)}.cc--image .cc__subtitle,.cc--image .cc__desc{color:var(--transparent-neutral-sys-white-80)}.ac{display:flex;flex:1 1 auto;align-items:center;gap:var(--spacing-padding-sys-padding-12);font-family:var(--body-medium-sys-font-family)}.ac__icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface);line-height:0}.ac__text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4);flex:1 1 auto;min-width:0}.ac__label{font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);line-height:var(--label-large-sys-line-height);font-weight:var(--label-large-sys-font-weight-emphasized);letter-spacing:var(--label-large-sys-tracking);color:var(--surface-surface-sys-on-surface);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ac__desc{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height);letter-spacing:var(--body-small-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.ac__arrow{display:inline-flex;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant);line-height:0}.ac--vertical{flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:var(--spacing-padding-sys-padding-16)}.ac__badge{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--surface-surface-container-sys-surface-container);color:var(--surface-surface-sys-on-surface);transition:background-color .14s ease-out;line-height:0}:host(:hover) .ac__badge{background-color:var(--surface-surface-container-sys-surface-container-lowest)}.ac--vertical .ac__label{white-space:pre-line;overflow:visible;font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight)}@media(prefers-reduced-motion:reduce){.ac__badge{transition:none}}.mc{display:flex;flex:1 1 auto;flex-direction:column;align-items:center;justify-content:center;gap:var(--spacing-padding-sys-padding-4);padding:var(--spacing-padding-sys-padding-8) 0;text-align:center;font-family:var(--body-medium-sys-font-family)}.mc__value{font-family:var(--headline-small-sys-font-family);font-size:var(--headline-small-sys-font-size);line-height:var(--headline-small-sys-line-height);font-weight:var(--headline-small-sys-font-weight-emphasized);letter-spacing:var(--headline-small-sys-tracking);color:var(--surface-surface-sys-on-surface)}.mc__label{font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.mc--current .mc__value,.mc--current .mc__label{color:var(--accent-primary-sys-primary)}\n"] }]
157
- }], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], radius: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], imageSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageSrc", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], plainIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "plainIcon", required: false }] }], category: [{ type: i0.Input, args: [{ isSignal: true, alias: "category", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], headline: [{ type: i0.Input, args: [{ isSignal: true, alias: "headline", required: false }] }], actionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionLabel", required: false }] }], showArrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "showArrow", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], trailingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingIcon", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], current: [{ type: i0.Input, args: [{ isSignal: true, alias: "current", required: false }] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }] } });
166
+ }, template: "@if (isImage() && mediaUrl()) {\r\n <div class=\"cmp-card__media\" [style.background-image]=\"mediaUrl()\"></div>\r\n <div class=\"cmp-card__scrim\" aria-hidden=\"true\"></div>\r\n}\r\n\r\n<div class=\"cmp-card__body\">\r\n @switch (variant()) {\r\n @case ('content') {\r\n @if (headline()) {\r\n <!-- Hero (image/filled): icon top-left, eyebrow + display headline + action bottom. -->\r\n <div class=\"cc cc--hero\" [class.cc--image]=\"isImage()\" [class.cc--filled]=\"appearance() === 'filled'\">\r\n @if (icon()) {\r\n @if (isImage()) {\r\n <div class=\"cc__media-icon\" [innerHTML]=\"iconSvg(icon(), 28)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__img-foot\">\r\n <div class=\"cc__hero-text\">\r\n @if (title()) { <p class=\"cc__eyebrow\">{{ title() }}</p> }\r\n <ng-container [ngTemplateOutlet]=\"headlineTpl\" />\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else if (isImage()) {\r\n <div class=\"cc cc--image\">\r\n <div class=\"cc__img-top\">\r\n @if (category()) { <span class=\"cc__chip cc__chip--glass\">{{ category() }}</span> }\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <ng-container [ngTemplateOutlet]=\"titleTpl\" /> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n <div class=\"cc__img-foot cc__img-foot--end\">\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"cc\" [class.cc--filled]=\"appearance() === 'filled'\" [class.cc--journey]=\"plainIcon()\">\r\n <div class=\"cc__top\">\r\n @if (icon()) {\r\n @if (plainIcon()) {\r\n <div class=\"cc__plain-icon\" [innerHTML]=\"iconSvg(icon(), 48, 1.5)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__top-end\">\r\n @if (category()) { <span class=\"cc__chip\">{{ category() }}</span> }\r\n @if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"cc__text\">\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <ng-container [ngTemplateOutlet]=\"titleTpl\" /> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) { <span class=\"cc__button\">{{ actionLabel() }}</span> }\r\n </div>\r\n }\r\n }\r\n\r\n @case ('action') {\r\n @if (orientation() === 'vertical') {\r\n <div class=\"ac ac--vertical\">\r\n @if (icon()) {\r\n <div class=\"ac__badge\" [innerHTML]=\"iconSvg(icon(), 24, 1.5)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <ng-container [ngTemplateOutlet]=\"labelTpl\" />\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"ac\">\r\n @if (icon()) {\r\n <div class=\"ac__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <ng-container [ngTemplateOutlet]=\"labelTpl\" />\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n <span class=\"ac__arrow\" [innerHTML]=\"iconSvg(trailingIcon(), 20)\" aria-hidden=\"true\"></span>\r\n </div>\r\n }\r\n }\r\n\r\n @case ('metric') {\r\n <div class=\"mc\" [class.mc--current]=\"current()\">\r\n <span class=\"mc__value\">{{ value() }}</span>\r\n @if (label()) { <span class=\"mc__label\">{{ label() }}</span> }\r\n </div>\r\n }\r\n\r\n @default {\r\n <ng-content />\r\n }\r\n }\r\n</div>\r\n\r\n<!-- Heading fragments: render a real <h2>\u2013<h6> when `headingLevel` is set, else the\r\n original <p>/<span>. Shared across layouts so semantics stay consistent and the\r\n cc__title / cc__headline / ac__label styling is unchanged. -->\r\n<ng-template #titleTpl>\r\n @switch (headingLevel()) {\r\n @case (2) { <h2 class=\"cc__title\">{{ title() }}</h2> }\r\n @case (3) { <h3 class=\"cc__title\">{{ title() }}</h3> }\r\n @case (4) { <h4 class=\"cc__title\">{{ title() }}</h4> }\r\n @case (5) { <h5 class=\"cc__title\">{{ title() }}</h5> }\r\n @case (6) { <h6 class=\"cc__title\">{{ title() }}</h6> }\r\n @default { <p class=\"cc__title\">{{ title() }}</p> }\r\n }\r\n</ng-template>\r\n\r\n<ng-template #headlineTpl>\r\n @switch (headingLevel()) {\r\n @case (2) { <h2 class=\"cc__headline\">{{ headline() }}</h2> }\r\n @case (3) { <h3 class=\"cc__headline\">{{ headline() }}</h3> }\r\n @case (4) { <h4 class=\"cc__headline\">{{ headline() }}</h4> }\r\n @case (5) { <h5 class=\"cc__headline\">{{ headline() }}</h5> }\r\n @case (6) { <h6 class=\"cc__headline\">{{ headline() }}</h6> }\r\n @default { <p class=\"cc__headline\">{{ headline() }}</p> }\r\n }\r\n</ng-template>\r\n\r\n<ng-template #labelTpl>\r\n @switch (headingLevel()) {\r\n @case (2) { <h2 class=\"ac__label\">{{ label() }}</h2> }\r\n @case (3) { <h3 class=\"ac__label\">{{ label() }}</h3> }\r\n @case (4) { <h4 class=\"ac__label\">{{ label() }}</h4> }\r\n @case (5) { <h5 class=\"ac__label\">{{ label() }}</h5> }\r\n @case (6) { <h6 class=\"ac__label\">{{ label() }}</h6> }\r\n @default { <span class=\"ac__label\">{{ label() }}</span> }\r\n }\r\n</ng-template>\r\n", styles: [":host{position:relative;display:flex;flex-direction:column;box-sizing:border-box;overflow:hidden;border-radius:var(--border-radius-sys-radius-lg);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);isolation:isolate}:host.cmp-card--radius-md{border-radius:var(--border-radius-sys-radius-md)}:host.cmp-card--radius-lg{border-radius:var(--border-radius-sys-radius-lg)}:host.cmp-card--radius-xl{border-radius:var(--border-radius-sys-radius-xl)}.cmp-card__body{position:relative;z-index:1;display:flex;flex-direction:column;flex:1 1 auto;min-height:0}:host.cmp-card--pad-none .cmp-card__body{padding:0}:host.cmp-card--pad-sm .cmp-card__body{padding:var(--spacing-padding-sys-padding-16)}:host.cmp-card--pad-md .cmp-card__body{padding:var(--spacing-padding-sys-padding-20)}:host.cmp-card--pad-lg .cmp-card__body{padding:var(--spacing-padding-sys-padding-24)}:host.cmp-card--pad-xl .cmp-card__body{padding:var(--spacing-padding-sys-padding-32)}:host.cmp-card--outlined{background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host.cmp-card--filled{background-color:var(--accent-primary-sys-primary-container);color:var(--accent-primary-sys-on-primary-container)}:host.cmp-card--gradient{background-image:linear-gradient(180deg,var(--transparent-primary-sys-primary-08) 0%,var(--transparent-neutral-sys-white-10) 100%);background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host.cmp-card--image{color:var(--accent-primary-sys-on-primary-container);background-color:var(--surface-surface-container-sys-surface-container-high)}.cmp-card__media{position:absolute;inset:0;z-index:0;background-size:cover;background-position:center}.cmp-card__scrim{position:absolute;inset:0;z-index:0;background-image:linear-gradient(180deg,var(--transparent-neutral-sys-black-40) 0%,var(--transparent-neutral-sys-black-10) 32%,var(--transparent-neutral-sys-black-10) 64%,var(--transparent-neutral-sys-black-50) 100%);mix-blend-mode:multiply}:host.cmp-card--interactive{cursor:pointer;transition:box-shadow .14s ease-out,background-color .14s ease-out,border-color .14s ease-out}:host.cmp-card--interactive:focus-visible{outline:none;border-color:var(--add-on-primary-fixed-sys-on-primary-fixed-variant);box-shadow:0 0 0 var(--spacing-padding-sys-padding-4) var(--transparent-primary-sys-primary-08)}:host.cmp-card--interactive.cmp-card--outlined:hover,:host.cmp-card--interactive.cmp-card--gradient:hover{background-color:var(--surface-surface-container-sys-surface-container);box-shadow:0 .25rem 1rem #0000001a,0 .125rem .25rem #00000014}:host.cmp-card--interactive.cmp-card--filled:hover,:host.cmp-card--interactive.cmp-card--image:hover{box-shadow:0 .25rem 1rem #0000002e,0 .125rem .25rem #0000001f}:host.cmp-card--disabled{pointer-events:none;opacity:.38}@media(prefers-reduced-motion:reduce){:host.cmp-card--interactive{transition:none}}@media(forced-colors:active){:host{border:1px solid CanvasText;forced-color-adjust:none}:host.cmp-card--interactive:focus-visible{outline:2px solid Highlight;outline-offset:2px}}.cc{display:flex;flex-direction:column;flex:1 1 auto;gap:var(--spacing-padding-sys-padding-24);font-family:var(--body-medium-sys-font-family)}.cc__top{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--spacing-padding-sys-padding-8)}.cc__icon{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary);line-height:0}.cc__plain-icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface);line-height:0}.cc--journey{justify-content:space-between}.cc--journey .cc__title{font-weight:var(--title-small-sys-font-weight)}.cc__top-end{display:inline-flex;align-items:center;gap:var(--spacing-padding-sys-padding-8)}.cc__arrow{display:inline-flex;color:var(--surface-surface-sys-on-surface-variant);flex-shrink:0;line-height:0}.cc__text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-12)}.cc__heading{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4)}.cc__title{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight-emphasized);letter-spacing:var(--title-small-sys-tracking);color:var(--surface-surface-sys-on-surface)}.cc__subtitle{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight-emphasized);letter-spacing:var(--title-small-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.cc__desc{margin:0;font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height);letter-spacing:var(--body-medium-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.cc__chip{display:inline-flex;align-items:center;padding:var(--spacing-padding-sys-padding-2) var(--spacing-padding-sys-padding-12);border-radius:var(--border-radius-sys-radius-full);background-color:var(--custom-info-sys-info-container);color:var(--custom-info-sys-on-info-container);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);line-height:var(--label-medium-sys-line-height);white-space:nowrap}.cc__chip--glass{background-color:var(--transparent-neutral-sys-white-20);color:var(--accent-primary-sys-on-primary);-webkit-backdrop-filter:blur(.375rem);backdrop-filter:blur(.375rem)}.cc__button{align-self:flex-start;display:inline-flex;align-items:center;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-32);border-radius:var(--border-radius-sys-radius-full);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);font-weight:var(--label-medium-sys-font-weight-emphasized);line-height:var(--label-medium-sys-line-height);white-space:nowrap}.cc--filled .cc__icon{background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--accent-primary-sys-primary)}.cc--filled .cc__title{color:var(--accent-primary-sys-on-primary-container)}.cc--filled .cc__subtitle,.cc--filled .cc__desc,.cc--filled .cc__arrow{color:var(--accent-primary-sys-on-primary-container-variant)}.cc--filled .cc__button{border-color:transparent}.cc--image{position:relative;display:flex;flex-direction:column;flex:1 1 auto;min-height:12.5rem;justify-content:space-between;gap:var(--spacing-padding-sys-padding-24);padding:var(--spacing-padding-sys-padding-24)}.cc--hero{position:relative;display:flex;flex-direction:column;flex:1 1 auto;justify-content:space-between;gap:var(--spacing-padding-sys-padding-24);min-height:22.5rem;padding:var(--spacing-padding-sys-padding-32)}.cc__media-icon{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--transparent-neutral-sys-white-20);color:var(--accent-primary-sys-on-primary);-webkit-backdrop-filter:blur(.375rem);backdrop-filter:blur(.375rem);line-height:0}.cc__img-top{display:flex;flex-direction:column;align-items:flex-start;gap:var(--spacing-padding-sys-padding-12)}.cc__img-foot{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--spacing-padding-sys-padding-16)}.cc__img-foot--end{justify-content:flex-end}.cc__hero-text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-8);min-width:0}.cc__eyebrow{margin:0;font-family:var(--title-small-sys-font-family);font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);color:var(--accent-primary-sys-on-primary)}.cc__headline{margin:0;font-family:var(--headline-large-sys-font-family);font-size:var(--headline-large-sys-font-size);line-height:var(--headline-large-sys-line-height);font-weight:var(--headline-large-sys-font-weight-emphasized);letter-spacing:var(--headline-large-sys-tracking);color:var(--accent-primary-sys-on-primary)}.cc__button--solid{align-self:flex-end;flex-shrink:0;border-color:transparent;box-shadow:0 .125rem .25rem #0000000f,0 .125rem .5rem #0000000a}.cc--image .cc__title,.cc--image .cc__arrow{color:var(--accent-primary-sys-on-primary)}.cc--image .cc__subtitle,.cc--image .cc__desc{color:var(--transparent-neutral-sys-white-80)}.ac{display:flex;flex:1 1 auto;align-items:center;gap:var(--spacing-padding-sys-padding-12);font-family:var(--body-medium-sys-font-family)}.ac__icon{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--surface-surface-sys-on-surface);line-height:0}.ac__text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4);flex:1 1 auto;min-width:0}.ac__label{margin:0;font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);line-height:var(--label-large-sys-line-height);font-weight:var(--label-large-sys-font-weight-emphasized);letter-spacing:var(--label-large-sys-tracking);color:var(--surface-surface-sys-on-surface);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ac__desc{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height);letter-spacing:var(--body-small-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.ac__arrow{display:inline-flex;flex-shrink:0;color:var(--surface-surface-sys-on-surface-variant);line-height:0}.ac--vertical{flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:var(--spacing-padding-sys-padding-16)}.ac__badge{display:inline-flex;align-items:center;justify-content:center;width:var(--sys-size-xl);height:var(--sys-size-xl);flex-shrink:0;border-radius:var(--border-radius-sys-radius-full);background-color:var(--surface-surface-container-sys-surface-container);color:var(--surface-surface-sys-on-surface);transition:background-color .14s ease-out;line-height:0}:host(:hover) .ac__badge{background-color:var(--surface-surface-container-sys-surface-container-lowest)}.ac--vertical .ac__label{white-space:pre-line;overflow:visible;font-size:var(--title-small-sys-font-size);line-height:var(--title-small-sys-line-height);font-weight:var(--title-small-sys-font-weight)}@media(prefers-reduced-motion:reduce){.ac__badge{transition:none}}.mc{display:flex;flex:1 1 auto;flex-direction:column;align-items:center;justify-content:center;gap:var(--spacing-padding-sys-padding-4);padding:var(--spacing-padding-sys-padding-8) 0;text-align:center;font-family:var(--body-medium-sys-font-family)}.mc__value{font-family:var(--headline-small-sys-font-family);font-size:var(--headline-small-sys-font-size);line-height:var(--headline-small-sys-line-height);font-weight:var(--headline-small-sys-font-weight-emphasized);letter-spacing:var(--headline-small-sys-tracking);color:var(--surface-surface-sys-on-surface)}.mc__label{font-family:var(--label-medium-sys-font-family);font-size:var(--label-medium-sys-font-size);line-height:var(--label-medium-sys-line-height);letter-spacing:var(--label-medium-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.mc--current .mc__value,.mc--current .mc__label{color:var(--accent-primary-sys-primary)}\n"] }]
167
+ }], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], radius: [{ type: i0.Input, args: [{ isSignal: true, alias: "radius", required: false }] }], padding: [{ type: i0.Input, args: [{ isSignal: true, alias: "padding", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], imageSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "imageSrc", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], plainIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "plainIcon", required: false }] }], category: [{ type: i0.Input, args: [{ isSignal: true, alias: "category", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], headline: [{ type: i0.Input, args: [{ isSignal: true, alias: "headline", required: false }] }], actionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionLabel", required: false }] }], showArrow: [{ type: i0.Input, args: [{ isSignal: true, alias: "showArrow", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], trailingIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailingIcon", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], current: [{ type: i0.Input, args: [{ isSignal: true, alias: "current", required: false }] }], headingLevel: [{ type: i0.Input, args: [{ isSignal: true, alias: "headingLevel", required: false }] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }] } });
158
168
  //# sourceMappingURL=card.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"card.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/card-base/card.ts","../../../../../../packages/angular/src/lib/card-base/card.html"],"names":[],"mappings":"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","sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n effect,\r\n inject,\r\n input,\r\n output,\r\n} from '@angular/core';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\r\nimport { RippleDirective } from './ripple.directive';\r\n\r\n/** Card type — selects the projected/laid-out content. Mirrors the library's `variant` pattern. */\r\nexport type CardVariant = 'basic' | 'content' | 'action' | 'metric';\r\n/** Visual surface treatment. Mirrors the Figma \"Card Item\" Style property. */\r\nexport type CardAppearance = 'outlined' | 'filled' | 'gradient' | 'image';\r\n/** Corner radius token: `md` (12px), `lg` (16px), `xl` (24px). */\r\nexport type CardRadius = 'md' | 'lg' | 'xl';\r\n/** Inner padding token (used by `variant=\"basic\"`; typed variants size themselves). */\r\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg' | 'xl';\r\n/** Action-card layout. */\r\nexport type CardOrientation = 'horizontal' | 'vertical';\r\n\r\n/**\r\n * Card — the single Cards component for the ComPsych Design System.\r\n *\r\n * Figma: Core Components — \"Cards\" (file VFBn7KCDy3FSIlvhNo3ylq, 1117:9829).\r\n *\r\n * One component, switched by `variant` (like Accordion's `variant`):\r\n * - `basic` — a bare surface; project your own content.\r\n * - `content` — icon badge / image, category chip, title, subtitle, description,\r\n * action pill, arrow; hero (`headline`) and journey (`plainIcon`) layouts.\r\n * - `action` — compact interactive row (icon · label/description · arrow); horizontal/vertical.\r\n * - `metric` — centered value + label; `current` (selected) state.\r\n *\r\n * `appearance` is the orthogonal surface skin (outlined / filled / gradient / image).\r\n * When `interactive`, the whole card is a button (role/tabindex/keyboard) with the\r\n * hover / focus / pressed ripple. All values are sys design tokens.\r\n *\r\n * <compsych-card variant=\"content\" appearance=\"gradient\" icon=\"circle-play\"\r\n * category=\"Video\" title=\"Mindful breathing\" actionLabel=\"Watch\" [interactive]=\"true\" />\r\n * <compsych-card variant=\"metric\" value=\"24\" label=\"Sessions\" [current]=\"true\" [interactive]=\"true\" />\r\n * <compsych-card>Projected content</compsych-card>\r\n */\r\n@Component({\r\n selector: 'compsych-card',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n hostDirectives: [RippleDirective],\r\n templateUrl: './card.html',\r\n styleUrl: './card.css',\r\n host: {\r\n class: 'cmp-card',\r\n '[class.cmp-card--outlined]': \"appearance() === 'outlined'\",\r\n '[class.cmp-card--filled]': \"appearance() === 'filled'\",\r\n '[class.cmp-card--gradient]': \"appearance() === 'gradient'\",\r\n '[class.cmp-card--image]': \"appearance() === 'image'\",\r\n '[class.cmp-card--radius-md]': \"radius() === 'md'\",\r\n '[class.cmp-card--radius-lg]': \"radius() === 'lg'\",\r\n '[class.cmp-card--radius-xl]': \"radius() === 'xl'\",\r\n '[class.cmp-card--pad-none]': \"effectivePadding() === 'none'\",\r\n '[class.cmp-card--pad-sm]': \"effectivePadding() === 'sm'\",\r\n '[class.cmp-card--pad-md]': \"effectivePadding() === 'md'\",\r\n '[class.cmp-card--pad-lg]': \"effectivePadding() === 'lg'\",\r\n '[class.cmp-card--pad-xl]': \"effectivePadding() === 'xl'\",\r\n '[class.cmp-card--interactive]': 'interactive()',\r\n '[class.cmp-card--disabled]': 'disabled()',\r\n '[attr.role]': \"interactive() ? 'button' : null\",\r\n '[attr.tabindex]': 'interactive() && !disabled() ? 0 : null',\r\n '[attr.aria-disabled]': 'disabled() || null',\r\n '[attr.aria-pressed]': \"interactive() && variant() === 'metric' ? current() : null\",\r\n '[style.border-color]': \"variant() === 'metric' && current() ? 'var(--accent-primary-sys-primary)' : null\",\r\n '[style.border-width]': \"variant() === 'metric' && current() ? 'var(--border-width-sys-stroke-thick)' : null\",\r\n '(click)': 'onActivate()',\r\n '(keydown)': 'onKeyDown($event)',\r\n },\r\n})\r\nexport class CardComponent {\r\n // ── Surface (all variants) ──────────────────────────────\r\n /** Card type. Defaults to `basic` (projected content). */\r\n readonly variant = input<CardVariant>('basic');\r\n /** Surface treatment. Defaults to `outlined`. */\r\n readonly appearance = input<CardAppearance>('outlined');\r\n /** Corner radius token. Defaults to `lg` (16px). */\r\n readonly radius = input<CardRadius>('lg');\r\n /** Inner padding for `variant=\"basic\"`. Typed variants size themselves. */\r\n readonly padding = input<CardPadding>('lg');\r\n /** Whole-card click target: adds role/tabindex, hover/press states and ripple. */\r\n readonly interactive = input<boolean>(false);\r\n /** Disabled visual + non-interactive. */\r\n readonly disabled = input<boolean>(false);\r\n /** Background image URL for `appearance=\"image\"`. */\r\n readonly imageSrc = input<string>('');\r\n\r\n // ── Content variant ─────────────────────────────────────\r\n /** Lucide icon name (content badge / action leading icon). */\r\n readonly icon = input<string>('');\r\n /** Content: render the icon as a large plain glyph, text anchored bottom (journey layout). */\r\n readonly plainIcon = input<boolean>(false);\r\n /** Content: category chip text. */\r\n readonly category = input<string>('');\r\n /** Content: title. */\r\n readonly title = input<string>('');\r\n /** Content: subtitle. */\r\n readonly subtitle = input<string>('');\r\n /** Content/action: supporting description. */\r\n readonly description = input<string>('');\r\n /** Content: large hero display text (switches to the XL hero layout). */\r\n readonly headline = input<string>('');\r\n /** Content: presentational action pill label. */\r\n readonly actionLabel = input<string>('');\r\n /** Content: show the trailing arrow-up-right affordance. */\r\n readonly showArrow = input<boolean>(true);\r\n\r\n // ── Action variant ──────────────────────────────────────\r\n /** Action/metric: primary label. */\r\n readonly label = input<string>('');\r\n /** Action: trailing Lucide icon (horizontal layout). */\r\n readonly trailingIcon = input<string>('arrow-up-right');\r\n /** Action: layout orientation. */\r\n readonly orientation = input<CardOrientation>('horizontal');\r\n\r\n // ── Metric variant ──────────────────────────────────────\r\n /** Metric: the prominent value. */\r\n readonly value = input<string | number>('');\r\n /** Metric: selected/current state (primary border + text). */\r\n readonly current = input<boolean>(false);\r\n\r\n /** Emits on click / Enter / Space when interactive and not disabled. */\r\n readonly cardClick = output<void>();\r\n\r\n private readonly sanitizer = inject(DomSanitizer);\r\n private readonly ripple = inject(RippleDirective);\r\n\r\n readonly isImage = computed(() => this.appearance() === 'image');\r\n\r\n /** CSS `url(...)` for the media layer, or null when no image is set. */\r\n readonly mediaUrl = computed(() => {\r\n const src = this.imageSrc();\r\n return src ? `url(\"${src}\")` : null;\r\n });\r\n\r\n /** Padding resolved per variant; `basic` honors the `padding` input. */\r\n readonly effectivePadding = computed<CardPadding>(() => {\r\n switch (this.variant()) {\r\n case 'content':\r\n return this.isImage() || this.headline() ? 'none' : this.plainIcon() ? 'xl' : 'lg';\r\n case 'action':\r\n return this.orientation() === 'vertical' ? 'lg' : 'sm';\r\n case 'metric':\r\n return 'sm';\r\n default:\r\n return this.padding();\r\n }\r\n });\r\n\r\n constructor() {\r\n effect(() => {\r\n this.ripple.suppressed = !this.interactive() || this.disabled();\r\n });\r\n }\r\n\r\n onActivate(): void {\r\n if (!this.interactive() || this.disabled()) return;\r\n this.cardClick.emit();\r\n }\r\n\r\n onKeyDown(event: KeyboardEvent): void {\r\n if (!this.interactive() || this.disabled()) return;\r\n if (event.repeat) return;\r\n if (event.key !== 'Enter' && event.key !== ' ') return;\r\n event.preventDefault();\r\n this.cardClick.emit();\r\n }\r\n\r\n /** Renders a sized/weighted Lucide SVG for template use. */\r\n iconSvg(name: string, size: number, strokeWidth = 1.75): SafeHtml {\r\n let svg = LUCIDE_ICONS[name] ?? '';\r\n svg = svg.replace('<svg ', `<svg width=\"${size}\" height=\"${size}\" `);\r\n if (strokeWidth !== 1.5) {\r\n svg = svg.replace('stroke-width=\"1.5\"', `stroke-width=\"${strokeWidth}\"`);\r\n }\r\n return this.sanitizer.bypassSecurityTrustHtml(svg);\r\n }\r\n}\r\n","@if (isImage() && mediaUrl()) {\r\n <div class=\"cmp-card__media\" [style.background-image]=\"mediaUrl()\"></div>\r\n <div class=\"cmp-card__scrim\" aria-hidden=\"true\"></div>\r\n}\r\n\r\n<div class=\"cmp-card__body\">\r\n @switch (variant()) {\r\n @case ('content') {\r\n @if (headline()) {\r\n <!-- Hero (image/filled): icon top-left, eyebrow + display headline + action bottom. -->\r\n <div class=\"cc cc--hero\" [class.cc--image]=\"isImage()\" [class.cc--filled]=\"appearance() === 'filled'\">\r\n @if (icon()) {\r\n @if (isImage()) {\r\n <div class=\"cc__media-icon\" [innerHTML]=\"iconSvg(icon(), 28)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__img-foot\">\r\n <div class=\"cc__hero-text\">\r\n @if (title()) { <p class=\"cc__eyebrow\">{{ title() }}</p> }\r\n <p class=\"cc__headline\">{{ headline() }}</p>\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else if (isImage()) {\r\n <div class=\"cc cc--image\">\r\n <div class=\"cc__img-top\">\r\n @if (category()) { <span class=\"cc__chip cc__chip--glass\">{{ category() }}</span> }\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <p class=\"cc__title\">{{ title() }}</p> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n <div class=\"cc__img-foot cc__img-foot--end\">\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"cc\" [class.cc--filled]=\"appearance() === 'filled'\" [class.cc--journey]=\"plainIcon()\">\r\n <div class=\"cc__top\">\r\n @if (icon()) {\r\n @if (plainIcon()) {\r\n <div class=\"cc__plain-icon\" [innerHTML]=\"iconSvg(icon(), 48, 1.5)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__top-end\">\r\n @if (category()) { <span class=\"cc__chip\">{{ category() }}</span> }\r\n @if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"cc__text\">\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <p class=\"cc__title\">{{ title() }}</p> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) { <span class=\"cc__button\">{{ actionLabel() }}</span> }\r\n </div>\r\n }\r\n }\r\n\r\n @case ('action') {\r\n @if (orientation() === 'vertical') {\r\n <div class=\"ac ac--vertical\">\r\n @if (icon()) {\r\n <div class=\"ac__badge\" [innerHTML]=\"iconSvg(icon(), 24, 1.5)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <span class=\"ac__label\">{{ label() }}</span>\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"ac\">\r\n @if (icon()) {\r\n <div class=\"ac__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <span class=\"ac__label\">{{ label() }}</span>\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n <span class=\"ac__arrow\" [innerHTML]=\"iconSvg(trailingIcon(), 20)\" aria-hidden=\"true\"></span>\r\n </div>\r\n }\r\n }\r\n\r\n @case ('metric') {\r\n <div class=\"mc\" [class.mc--current]=\"current()\">\r\n <span class=\"mc__value\">{{ value() }}</span>\r\n @if (label()) { <span class=\"mc__label\">{{ label() }}</span> }\r\n </div>\r\n }\r\n\r\n @default {\r\n <ng-content />\r\n }\r\n }\r\n</div>\r\n"]}
1
+ {"version":3,"file":"card.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/card-base/card.ts","../../../../../../packages/angular/src/lib/card-base/card.html"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,SAAS,EACT,QAAQ,EACR,MAAM,EACN,MAAM,EACN,KAAK,EACL,MAAM,GACP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAY,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;;;AAerD;;;;;;;;;;;;;;;;;;;;GAoBG;AAmCH,MAAM,OAAO,aAAa;IACxB,2DAA2D;IAC3D,0DAA0D;IACjD,OAAO,GAAG,KAAK,CAAc,OAAO,8EAAC,CAAC;IAC/C,iDAAiD;IACxC,UAAU,GAAG,KAAK,CAAiB,UAAU,iFAAC,CAAC;IACxD,oDAAoD;IAC3C,MAAM,GAAG,KAAK,CAAa,IAAI,6EAAC,CAAC;IAC1C,2EAA2E;IAClE,OAAO,GAAG,KAAK,CAAc,IAAI,8EAAC,CAAC;IAC5C,kFAAkF;IACzE,WAAW,GAAG,KAAK,CAAU,KAAK,kFAAC,CAAC;IAC7C,yCAAyC;IAChC,QAAQ,GAAG,KAAK,CAAU,KAAK,+EAAC,CAAC;IAC1C,qDAAqD;IAC5C,QAAQ,GAAG,KAAK,CAAS,EAAE,+EAAC,CAAC;IAEtC,2DAA2D;IAC3D,8DAA8D;IACrD,IAAI,GAAG,KAAK,CAAS,EAAE,2EAAC,CAAC;IAClC,8FAA8F;IACrF,SAAS,GAAG,KAAK,CAAU,KAAK,gFAAC,CAAC;IAC3C,mCAAmC;IAC1B,QAAQ,GAAG,KAAK,CAAS,EAAE,+EAAC,CAAC;IACtC,sBAAsB;IACb,KAAK,GAAG,KAAK,CAAS,EAAE,4EAAC,CAAC;IACnC,yBAAyB;IAChB,QAAQ,GAAG,KAAK,CAAS,EAAE,+EAAC,CAAC;IACtC,8CAA8C;IACrC,WAAW,GAAG,KAAK,CAAS,EAAE,kFAAC,CAAC;IACzC,yEAAyE;IAChE,QAAQ,GAAG,KAAK,CAAS,EAAE,+EAAC,CAAC;IACtC,iDAAiD;IACxC,WAAW,GAAG,KAAK,CAAS,EAAE,kFAAC,CAAC;IACzC,4DAA4D;IACnD,SAAS,GAAG,KAAK,CAAU,IAAI,gFAAC,CAAC;IAE1C,2DAA2D;IAC3D,oCAAoC;IAC3B,KAAK,GAAG,KAAK,CAAS,EAAE,4EAAC,CAAC;IACnC,wDAAwD;IAC/C,YAAY,GAAG,KAAK,CAAS,gBAAgB,mFAAC,CAAC;IACxD,kCAAkC;IACzB,WAAW,GAAG,KAAK,CAAkB,YAAY,kFAAC,CAAC;IAE5D,2DAA2D;IAC3D,mCAAmC;IAC1B,KAAK,GAAG,KAAK,CAAkB,EAAE,4EAAC,CAAC;IAC5C,8DAA8D;IACrD,OAAO,GAAG,KAAK,CAAU,KAAK,8EAAC,CAAC;IAEzC,2DAA2D;IAC3D;;;;;;OAMG;IACM,YAAY,GAAG,KAAK,CAA+B,SAAS,mFAAC,CAAC;IAEvE,wEAAwE;IAC/D,SAAS,GAAG,MAAM,EAAQ,CAAC;IAEnB,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IACjC,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;IAEzC,OAAO,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,OAAO,8EAAC,CAAC;IAEjE,wEAAwE;IAC/D,QAAQ,GAAG,QAAQ,CAAC,GAAG,EAAE;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACtC,CAAC,+EAAC,CAAC;IAEH,wEAAwE;IAC/D,gBAAgB,GAAG,QAAQ,CAAc,GAAG,EAAE;QACrD,QAAQ,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACvB,KAAK,SAAS;gBACZ,OAAO,IAAI,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YACrF,KAAK,QAAQ;gBACX,OAAO,IAAI,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;YACzD,KAAK,QAAQ;gBACX,OAAO,IAAI,CAAC;YACd;gBACE,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC,uFAAC,CAAC;IAEH;QACE,MAAM,CAAC,GAAG,EAAE;YACV,IAAI,CAAC,MAAM,CAAC,UAAU,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClE,CAAC,CAAC,CAAC;IACL,CAAC;IAED,UAAU;QACR,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE,OAAO;QACnD,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,SAAS,CAAC,KAAoB;QAC5B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE,OAAO;QACnD,IAAI,KAAK,CAAC,MAAM;YAAE,OAAO;QACzB,IAAI,KAAK,CAAC,GAAG,KAAK,OAAO,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG;YAAE,OAAO;QACvD,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,4DAA4D;IAC5D,OAAO,CAAC,IAAY,EAAE,IAAY,EAAE,WAAW,GAAG,IAAI;QACpD,IAAI,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,IAAI,aAAa,IAAI,IAAI,CAAC,CAAC;QACrE,IAAI,WAAW,KAAK,GAAG,EAAE,CAAC;YACxB,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,oBAAoB,EAAE,iBAAiB,WAAW,GAAG,CAAC,CAAC;QAC3E,CAAC;QACD,OAAO,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC;wGApHU,aAAa;4FAAb,aAAa,0wIClF1B,s8NA2JA,grXDvGY,gBAAgB;;4FA8Bf,aAAa;kBAlCzB,SAAS;+BACE,eAAe,cACb,IAAI,mBACC,uBAAuB,CAAC,MAAM,WACtC,CAAC,gBAAgB,CAAC,kBACX,CAAC,eAAe,CAAC,QAG3B;wBACJ,KAAK,EAAE,UAAU;wBACjB,4BAA4B,EAAE,6BAA6B;wBAC3D,0BAA0B,EAAE,2BAA2B;wBACvD,4BAA4B,EAAE,6BAA6B;wBAC3D,yBAAyB,EAAE,0BAA0B;wBACrD,6BAA6B,EAAE,mBAAmB;wBAClD,6BAA6B,EAAE,mBAAmB;wBAClD,6BAA6B,EAAE,mBAAmB;wBAClD,4BAA4B,EAAE,+BAA+B;wBAC7D,0BAA0B,EAAE,6BAA6B;wBACzD,0BAA0B,EAAE,6BAA6B;wBACzD,0BAA0B,EAAE,6BAA6B;wBACzD,0BAA0B,EAAE,6BAA6B;wBACzD,+BAA+B,EAAE,eAAe;wBAChD,4BAA4B,EAAE,YAAY;wBAC1C,aAAa,EAAE,iCAAiC;wBAChD,iBAAiB,EAAE,yCAAyC;wBAC5D,sBAAsB,EAAE,oBAAoB;wBAC5C,qBAAqB,EAAE,4DAA4D;wBACnF,sBAAsB,EAAE,kFAAkF;wBAC1G,sBAAsB,EAAE,qFAAqF;wBAC7G,SAAS,EAAE,cAAc;wBACzB,WAAW,EAAE,mBAAmB;qBACjC","sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n effect,\r\n inject,\r\n input,\r\n output,\r\n} from '@angular/core';\r\nimport { NgTemplateOutlet } from '@angular/common';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\r\nimport { RippleDirective } from './ripple.directive';\r\n\r\n/** Card type — selects the projected/laid-out content. Mirrors the library's `variant` pattern. */\r\nexport type CardVariant = 'basic' | 'content' | 'action' | 'metric';\r\n/** Visual surface treatment. Mirrors the Figma \"Card Item\" Style property. */\r\nexport type CardAppearance = 'outlined' | 'filled' | 'gradient' | 'image';\r\n/** Corner radius token: `md` (12px), `lg` (16px), `xl` (24px). */\r\nexport type CardRadius = 'md' | 'lg' | 'xl';\r\n/** Inner padding token (used by `variant=\"basic\"`; typed variants size themselves). */\r\nexport type CardPadding = 'none' | 'sm' | 'md' | 'lg' | 'xl';\r\n/** Action-card layout. */\r\nexport type CardOrientation = 'horizontal' | 'vertical';\r\n/** Heading level for the title / hero headline / action label (screen-reader heading semantics). */\r\nexport type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;\r\n\r\n/**\r\n * Card — the single Cards component for the ComPsych Design System.\r\n *\r\n * Figma: Core Components — \"Cards\" (file VFBn7KCDy3FSIlvhNo3ylq, 1117:9829).\r\n *\r\n * One component, switched by `variant` (like Accordion's `variant`):\r\n * - `basic` — a bare surface; project your own content.\r\n * - `content` — icon badge / image, category chip, title, subtitle, description,\r\n * action pill, arrow; hero (`headline`) and journey (`plainIcon`) layouts.\r\n * - `action` — compact interactive row (icon · label/description · arrow); horizontal/vertical.\r\n * - `metric` — centered value + label; `current` (selected) state.\r\n *\r\n * `appearance` is the orthogonal surface skin (outlined / filled / gradient / image).\r\n * When `interactive`, the whole card is a button (role/tabindex/keyboard) with the\r\n * hover / focus / pressed ripple. All values are sys design tokens.\r\n *\r\n * <compsych-card variant=\"content\" appearance=\"gradient\" icon=\"circle-play\"\r\n * category=\"Video\" title=\"Mindful breathing\" actionLabel=\"Watch\" [interactive]=\"true\" />\r\n * <compsych-card variant=\"metric\" value=\"24\" label=\"Sessions\" [current]=\"true\" [interactive]=\"true\" />\r\n * <compsych-card>Projected content</compsych-card>\r\n */\r\n@Component({\r\n selector: 'compsych-card',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n imports: [NgTemplateOutlet],\r\n hostDirectives: [RippleDirective],\r\n templateUrl: './card.html',\r\n styleUrl: './card.css',\r\n host: {\r\n class: 'cmp-card',\r\n '[class.cmp-card--outlined]': \"appearance() === 'outlined'\",\r\n '[class.cmp-card--filled]': \"appearance() === 'filled'\",\r\n '[class.cmp-card--gradient]': \"appearance() === 'gradient'\",\r\n '[class.cmp-card--image]': \"appearance() === 'image'\",\r\n '[class.cmp-card--radius-md]': \"radius() === 'md'\",\r\n '[class.cmp-card--radius-lg]': \"radius() === 'lg'\",\r\n '[class.cmp-card--radius-xl]': \"radius() === 'xl'\",\r\n '[class.cmp-card--pad-none]': \"effectivePadding() === 'none'\",\r\n '[class.cmp-card--pad-sm]': \"effectivePadding() === 'sm'\",\r\n '[class.cmp-card--pad-md]': \"effectivePadding() === 'md'\",\r\n '[class.cmp-card--pad-lg]': \"effectivePadding() === 'lg'\",\r\n '[class.cmp-card--pad-xl]': \"effectivePadding() === 'xl'\",\r\n '[class.cmp-card--interactive]': 'interactive()',\r\n '[class.cmp-card--disabled]': 'disabled()',\r\n '[attr.role]': \"interactive() ? 'button' : null\",\r\n '[attr.tabindex]': 'interactive() && !disabled() ? 0 : null',\r\n '[attr.aria-disabled]': 'disabled() || null',\r\n '[attr.aria-pressed]': \"interactive() && variant() === 'metric' ? current() : null\",\r\n '[style.border-color]': \"variant() === 'metric' && current() ? 'var(--accent-primary-sys-primary)' : null\",\r\n '[style.border-width]': \"variant() === 'metric' && current() ? 'var(--border-width-sys-stroke-thick)' : null\",\r\n '(click)': 'onActivate()',\r\n '(keydown)': 'onKeyDown($event)',\r\n },\r\n})\r\nexport class CardComponent {\r\n // ── Surface (all variants) ──────────────────────────────\r\n /** Card type. Defaults to `basic` (projected content). */\r\n readonly variant = input<CardVariant>('basic');\r\n /** Surface treatment. Defaults to `outlined`. */\r\n readonly appearance = input<CardAppearance>('outlined');\r\n /** Corner radius token. Defaults to `lg` (16px). */\r\n readonly radius = input<CardRadius>('lg');\r\n /** Inner padding for `variant=\"basic\"`. Typed variants size themselves. */\r\n readonly padding = input<CardPadding>('lg');\r\n /** Whole-card click target: adds role/tabindex, hover/press states and ripple. */\r\n readonly interactive = input<boolean>(false);\r\n /** Disabled visual + non-interactive. */\r\n readonly disabled = input<boolean>(false);\r\n /** Background image URL for `appearance=\"image\"`. */\r\n readonly imageSrc = input<string>('');\r\n\r\n // ── Content variant ─────────────────────────────────────\r\n /** Lucide icon name (content badge / action leading icon). */\r\n readonly icon = input<string>('');\r\n /** Content: render the icon as a large plain glyph, text anchored bottom (journey layout). */\r\n readonly plainIcon = input<boolean>(false);\r\n /** Content: category chip text. */\r\n readonly category = input<string>('');\r\n /** Content: title. */\r\n readonly title = input<string>('');\r\n /** Content: subtitle. */\r\n readonly subtitle = input<string>('');\r\n /** Content/action: supporting description. */\r\n readonly description = input<string>('');\r\n /** Content: large hero display text (switches to the XL hero layout). */\r\n readonly headline = input<string>('');\r\n /** Content: presentational action pill label. */\r\n readonly actionLabel = input<string>('');\r\n /** Content: show the trailing arrow-up-right affordance. */\r\n readonly showArrow = input<boolean>(true);\r\n\r\n // ── Action variant ──────────────────────────────────────\r\n /** Action/metric: primary label. */\r\n readonly label = input<string>('');\r\n /** Action: trailing Lucide icon (horizontal layout). */\r\n readonly trailingIcon = input<string>('arrow-up-right');\r\n /** Action: layout orientation. */\r\n readonly orientation = input<CardOrientation>('horizontal');\r\n\r\n // ── Metric variant ──────────────────────────────────────\r\n /** Metric: the prominent value. */\r\n readonly value = input<string | number>('');\r\n /** Metric: selected/current state (primary border + text). */\r\n readonly current = input<boolean>(false);\r\n\r\n // ── Accessibility ───────────────────────────────────────\r\n /**\r\n * Opts the title / hero headline / action label into a heading in the\r\n * accessibility tree via `role=\"heading\"` + `aria-level`, so consumers that\r\n * replace real headings keep heading semantics and screen-reader heading\r\n * navigation (WCAG 1.3.1). Omit (default) to render as plain text — no visual\r\n * or behavioral change for existing usages.\r\n */\r\n readonly headingLevel = input<CardHeadingLevel | undefined>(undefined);\r\n\r\n /** Emits on click / Enter / Space when interactive and not disabled. */\r\n readonly cardClick = output<void>();\r\n\r\n private readonly sanitizer = inject(DomSanitizer);\r\n private readonly ripple = inject(RippleDirective);\r\n\r\n readonly isImage = computed(() => this.appearance() === 'image');\r\n\r\n /** CSS `url(...)` for the media layer, or null when no image is set. */\r\n readonly mediaUrl = computed(() => {\r\n const src = this.imageSrc();\r\n return src ? `url(\"${src}\")` : null;\r\n });\r\n\r\n /** Padding resolved per variant; `basic` honors the `padding` input. */\r\n readonly effectivePadding = computed<CardPadding>(() => {\r\n switch (this.variant()) {\r\n case 'content':\r\n return this.isImage() || this.headline() ? 'none' : this.plainIcon() ? 'xl' : 'lg';\r\n case 'action':\r\n return this.orientation() === 'vertical' ? 'lg' : 'sm';\r\n case 'metric':\r\n return 'sm';\r\n default:\r\n return this.padding();\r\n }\r\n });\r\n\r\n constructor() {\r\n effect(() => {\r\n this.ripple.suppressed = !this.interactive() || this.disabled();\r\n });\r\n }\r\n\r\n onActivate(): void {\r\n if (!this.interactive() || this.disabled()) return;\r\n this.cardClick.emit();\r\n }\r\n\r\n onKeyDown(event: KeyboardEvent): void {\r\n if (!this.interactive() || this.disabled()) return;\r\n if (event.repeat) return;\r\n if (event.key !== 'Enter' && event.key !== ' ') return;\r\n event.preventDefault();\r\n this.cardClick.emit();\r\n }\r\n\r\n /** Renders a sized/weighted Lucide SVG for template use. */\r\n iconSvg(name: string, size: number, strokeWidth = 1.75): SafeHtml {\r\n let svg = LUCIDE_ICONS[name] ?? '';\r\n svg = svg.replace('<svg ', `<svg width=\"${size}\" height=\"${size}\" `);\r\n if (strokeWidth !== 1.5) {\r\n svg = svg.replace('stroke-width=\"1.5\"', `stroke-width=\"${strokeWidth}\"`);\r\n }\r\n return this.sanitizer.bypassSecurityTrustHtml(svg);\r\n }\r\n}\r\n","@if (isImage() && mediaUrl()) {\r\n <div class=\"cmp-card__media\" [style.background-image]=\"mediaUrl()\"></div>\r\n <div class=\"cmp-card__scrim\" aria-hidden=\"true\"></div>\r\n}\r\n\r\n<div class=\"cmp-card__body\">\r\n @switch (variant()) {\r\n @case ('content') {\r\n @if (headline()) {\r\n <!-- Hero (image/filled): icon top-left, eyebrow + display headline + action bottom. -->\r\n <div class=\"cc cc--hero\" [class.cc--image]=\"isImage()\" [class.cc--filled]=\"appearance() === 'filled'\">\r\n @if (icon()) {\r\n @if (isImage()) {\r\n <div class=\"cc__media-icon\" [innerHTML]=\"iconSvg(icon(), 28)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__img-foot\">\r\n <div class=\"cc__hero-text\">\r\n @if (title()) { <p class=\"cc__eyebrow\">{{ title() }}</p> }\r\n <ng-container [ngTemplateOutlet]=\"headlineTpl\" />\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else if (isImage()) {\r\n <div class=\"cc cc--image\">\r\n <div class=\"cc__img-top\">\r\n @if (category()) { <span class=\"cc__chip cc__chip--glass\">{{ category() }}</span> }\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <ng-container [ngTemplateOutlet]=\"titleTpl\" /> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n <div class=\"cc__img-foot cc__img-foot--end\">\r\n @if (actionLabel()) {\r\n <span class=\"cc__button cc__button--solid\">{{ actionLabel() }}</span>\r\n } @else if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"cc\" [class.cc--filled]=\"appearance() === 'filled'\" [class.cc--journey]=\"plainIcon()\">\r\n <div class=\"cc__top\">\r\n @if (icon()) {\r\n @if (plainIcon()) {\r\n <div class=\"cc__plain-icon\" [innerHTML]=\"iconSvg(icon(), 48, 1.5)\" aria-hidden=\"true\"></div>\r\n } @else {\r\n <div class=\"cc__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n }\r\n <div class=\"cc__top-end\">\r\n @if (category()) { <span class=\"cc__chip\">{{ category() }}</span> }\r\n @if (showArrow()) {\r\n <span class=\"cc__arrow\" [innerHTML]=\"iconSvg('arrow-up-right', 24)\" aria-hidden=\"true\"></span>\r\n }\r\n </div>\r\n </div>\r\n <div class=\"cc__text\">\r\n @if (title() || subtitle()) {\r\n <div class=\"cc__heading\">\r\n @if (title()) { <ng-container [ngTemplateOutlet]=\"titleTpl\" /> }\r\n @if (subtitle()) { <p class=\"cc__subtitle\">{{ subtitle() }}</p> }\r\n </div>\r\n }\r\n @if (description()) { <p class=\"cc__desc\">{{ description() }}</p> }\r\n </div>\r\n @if (actionLabel()) { <span class=\"cc__button\">{{ actionLabel() }}</span> }\r\n </div>\r\n }\r\n }\r\n\r\n @case ('action') {\r\n @if (orientation() === 'vertical') {\r\n <div class=\"ac ac--vertical\">\r\n @if (icon()) {\r\n <div class=\"ac__badge\" [innerHTML]=\"iconSvg(icon(), 24, 1.5)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <ng-container [ngTemplateOutlet]=\"labelTpl\" />\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"ac\">\r\n @if (icon()) {\r\n <div class=\"ac__icon\" [innerHTML]=\"iconSvg(icon(), 24)\" aria-hidden=\"true\"></div>\r\n }\r\n <div class=\"ac__text\">\r\n <ng-container [ngTemplateOutlet]=\"labelTpl\" />\r\n @if (description()) { <span class=\"ac__desc\">{{ description() }}</span> }\r\n </div>\r\n <span class=\"ac__arrow\" [innerHTML]=\"iconSvg(trailingIcon(), 20)\" aria-hidden=\"true\"></span>\r\n </div>\r\n }\r\n }\r\n\r\n @case ('metric') {\r\n <div class=\"mc\" [class.mc--current]=\"current()\">\r\n <span class=\"mc__value\">{{ value() }}</span>\r\n @if (label()) { <span class=\"mc__label\">{{ label() }}</span> }\r\n </div>\r\n }\r\n\r\n @default {\r\n <ng-content />\r\n }\r\n }\r\n</div>\r\n\r\n<!-- Heading fragments: render a real <h2>–<h6> when `headingLevel` is set, else the\r\n original <p>/<span>. Shared across layouts so semantics stay consistent and the\r\n cc__title / cc__headline / ac__label styling is unchanged. -->\r\n<ng-template #titleTpl>\r\n @switch (headingLevel()) {\r\n @case (2) { <h2 class=\"cc__title\">{{ title() }}</h2> }\r\n @case (3) { <h3 class=\"cc__title\">{{ title() }}</h3> }\r\n @case (4) { <h4 class=\"cc__title\">{{ title() }}</h4> }\r\n @case (5) { <h5 class=\"cc__title\">{{ title() }}</h5> }\r\n @case (6) { <h6 class=\"cc__title\">{{ title() }}</h6> }\r\n @default { <p class=\"cc__title\">{{ title() }}</p> }\r\n }\r\n</ng-template>\r\n\r\n<ng-template #headlineTpl>\r\n @switch (headingLevel()) {\r\n @case (2) { <h2 class=\"cc__headline\">{{ headline() }}</h2> }\r\n @case (3) { <h3 class=\"cc__headline\">{{ headline() }}</h3> }\r\n @case (4) { <h4 class=\"cc__headline\">{{ headline() }}</h4> }\r\n @case (5) { <h5 class=\"cc__headline\">{{ headline() }}</h5> }\r\n @case (6) { <h6 class=\"cc__headline\">{{ headline() }}</h6> }\r\n @default { <p class=\"cc__headline\">{{ headline() }}</p> }\r\n }\r\n</ng-template>\r\n\r\n<ng-template #labelTpl>\r\n @switch (headingLevel()) {\r\n @case (2) { <h2 class=\"ac__label\">{{ label() }}</h2> }\r\n @case (3) { <h3 class=\"ac__label\">{{ label() }}</h3> }\r\n @case (4) { <h4 class=\"ac__label\">{{ label() }}</h4> }\r\n @case (5) { <h5 class=\"ac__label\">{{ label() }}</h5> }\r\n @case (6) { <h6 class=\"ac__label\">{{ label() }}</h6> }\r\n @default { <span class=\"ac__label\">{{ label() }}</span> }\r\n }\r\n</ng-template>\r\n"]}
@@ -0,0 +1,194 @@
1
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, booleanAttribute, inject, } from '@angular/core';
2
+ import { NgControl, Validators } from '@angular/forms';
3
+ import * as i0 from "@angular/core";
4
+ let nextId = 0;
5
+ /**
6
+ * A labelled text input that wraps a native `<input>` element.
7
+ * Implements `ControlValueAccessor` so it works with both reactive forms
8
+ * (`formControlName`, `[formControl]`) and template-driven forms (`ngModel`).
9
+ * The error visual state is driven automatically by the bound `FormControl`'s
10
+ * validity once the field has been touched, and can also be set explicitly via
11
+ * the `error` input for use outside of Angular forms.
12
+ */
13
+ export class TextInputComponent {
14
+ _autoId = `cmp-text-input-${++nextId}`;
15
+ cdr = inject(ChangeDetectorRef);
16
+ _onChange = () => { };
17
+ _onTouched = () => { };
18
+ /** Injected when the component is used inside a reactive or template-driven form. */
19
+ ngControl;
20
+ constructor() {
21
+ const ctrl = inject(NgControl, { optional: true, self: true });
22
+ if (ctrl)
23
+ ctrl.valueAccessor = this;
24
+ this.ngControl = ctrl;
25
+ }
26
+ /** Label displayed above the input. */
27
+ label = '';
28
+ /** Placeholder text shown inside the input when empty. */
29
+ placeholder = '';
30
+ /** Current value of the input. When used in a form, the form controls the value. */
31
+ value = '';
32
+ /** Native input type. Defaults to `'text'`. Use `'password'` to mask the value. */
33
+ type = 'text';
34
+ /** Size variant — controls height, padding, and font size. Defaults to `'medium'`. */
35
+ size = 'medium';
36
+ /** Native `name` attribute forwarded to the `<input>`. Defaults to the auto-generated id. */
37
+ name;
38
+ /** Native `id` attribute for the `<input>`. Auto-generated if omitted. */
39
+ id;
40
+ /**
41
+ * Id(s) of external elements that describe the input, space-separated.
42
+ * Forwarded to `aria-describedby` on the native `<input>`.
43
+ */
44
+ ariaDescribedBy;
45
+ /**
46
+ * Adds a blue asterisk after the label. When used in a form, this is set
47
+ * automatically if the bound `FormControl` has `Validators.required`.
48
+ * Can also be set explicitly for non-form use. Supports boolean coercion.
49
+ */
50
+ required = false;
51
+ /**
52
+ * Applies the error visual state (red border) explicitly. When used in a form,
53
+ * the error state is driven automatically from the `FormControl`'s validity once
54
+ * the field is touched — this input is only needed outside of forms.
55
+ * Supports boolean coercion.
56
+ */
57
+ error = false;
58
+ /** Disables the input. When used in a form, also responds to `FormControl.disable()`. Supports boolean coercion. */
59
+ disabled = false;
60
+ /** Makes the input read-only — focusable and copyable but not editable. Value is still submitted with the form. Supports boolean coercion. */
61
+ readonly = false;
62
+ /** Emits the new string value whenever the user types. For use outside Angular forms; inside forms use the `FormControl` value stream. */
63
+ valueChange = new EventEmitter();
64
+ get inputId() {
65
+ return this.id ?? this._autoId;
66
+ }
67
+ /** True when the bound FormControl is invalid and touched, or when `error` is explicitly set. */
68
+ get hasError() {
69
+ return this.error || !!(this.ngControl?.invalid && this.ngControl.touched);
70
+ }
71
+ /** True when `required` is set explicitly, or when the bound FormControl has Validators.required. */
72
+ get isRequired() {
73
+ return this.required || !!(this.ngControl?.control?.hasValidator(Validators.required));
74
+ }
75
+ get wrapperClasses() {
76
+ return ['cmp-text-field', this.size, this.disabled ? 'disabled' : '']
77
+ .filter(Boolean)
78
+ .join(' ');
79
+ }
80
+ // ── ControlValueAccessor ────────────────────────────────────────────────────
81
+ writeValue(value) {
82
+ this.value = value ?? '';
83
+ this.cdr.markForCheck();
84
+ }
85
+ registerOnChange(fn) {
86
+ this._onChange = fn;
87
+ }
88
+ registerOnTouched(fn) {
89
+ this._onTouched = fn;
90
+ }
91
+ /** Called by Angular when the bound FormControl is programmatically disabled or enabled. */
92
+ setDisabledState(isDisabled) {
93
+ this.disabled = isDisabled;
94
+ this.cdr.markForCheck();
95
+ }
96
+ // ── Event handlers ──────────────────────────────────────────────────────────
97
+ onInput(event) {
98
+ const value = event.target.value;
99
+ this.value = value;
100
+ this._onChange(value);
101
+ this.valueChange.emit(value);
102
+ }
103
+ onBlur() {
104
+ this._onTouched();
105
+ this.cdr.markForCheck();
106
+ }
107
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TextInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
108
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: TextInputComponent, isStandalone: true, selector: "compsych-text-input", inputs: { label: "label", placeholder: "placeholder", value: "value", type: "type", size: "size", name: "name", id: "id", ariaDescribedBy: "ariaDescribedBy", required: ["required", "required", booleanAttribute], error: ["error", "error", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], readonly: ["readonly", "readonly", booleanAttribute] }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
109
+ <div [class]="wrapperClasses">
110
+ <label class="label" [for]="inputId">
111
+ {{ label }}
112
+ @if (isRequired) {
113
+ <span class="required-marker" aria-hidden="true"> *</span>
114
+ }
115
+ </label>
116
+ <input
117
+ class="input"
118
+ [id]="inputId"
119
+ [name]="name || inputId"
120
+ [type]="type"
121
+ [placeholder]="placeholder"
122
+ [value]="value"
123
+ [disabled]="disabled"
124
+ [readOnly]="readonly"
125
+ [attr.required]="isRequired ? '' : null"
126
+ [class.error]="hasError"
127
+ [attr.aria-invalid]="hasError ? 'true' : null"
128
+ [attr.aria-describedby]="ariaDescribedBy || null"
129
+ (input)="onInput($event)"
130
+ (blur)="onBlur()"
131
+ />
132
+ </div>
133
+ `, isInline: true, styles: [":host{display:block}.cmp-text-field{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4)}.label{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface)}.required-marker{color:var(--accent-primary-sys-primary)}.cmp-text-field.disabled .label{opacity:.48}.input{box-sizing:border-box;width:100%;margin:0;border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-sm);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);font-weight:var(--body-medium-sys-font-weight);line-height:var(--body-medium-sys-line-height);appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.input::placeholder{color:var(--surface-surface-sys-on-surface-variant)}.input:hover:not(:disabled):not(:focus){border-color:var(--outline-sys-outline-variant);box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.input:focus{border-color:var(--accent-primary-sys-primary);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input.error:not(:disabled):not(:focus){border-color:var(--error-sys-error)}.input.error:focus{border-color:var(--error-sys-error);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input:disabled{background-color:var(--surface-surface-container-sys-surface-container-highest);color:var(--surface-surface-sys-on-surface-variant);border-color:var(--outline-sys-outline);cursor:not-allowed;opacity:.48}.cmp-text-field.small .input{height:44px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-12);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.cmp-text-field.medium .input{height:52px;padding:var(--spacing-padding-sys-padding-12) var(--spacing-padding-sys-padding-16);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.cmp-text-field.large .input{height:64px;padding:var(--spacing-padding-sys-padding-20) var(--spacing-padding-sys-padding-16);font-size:var(--body-large-sys-font-size);line-height:var(--body-large-sys-line-height)}@media(forced-colors:active){.input{border:1px solid ButtonText;forced-color-adjust:none}.input:focus{outline:2px solid Highlight;outline-offset:2px}.input:disabled{color:GrayText;border-color:GrayText;background:ButtonFace;opacity:1}.cmp-text-field.disabled .label{color:GrayText;opacity:1}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
134
+ }
135
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: TextInputComponent, decorators: [{
136
+ type: Component,
137
+ args: [{ selector: 'compsych-text-input', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
138
+ <div [class]="wrapperClasses">
139
+ <label class="label" [for]="inputId">
140
+ {{ label }}
141
+ @if (isRequired) {
142
+ <span class="required-marker" aria-hidden="true"> *</span>
143
+ }
144
+ </label>
145
+ <input
146
+ class="input"
147
+ [id]="inputId"
148
+ [name]="name || inputId"
149
+ [type]="type"
150
+ [placeholder]="placeholder"
151
+ [value]="value"
152
+ [disabled]="disabled"
153
+ [readOnly]="readonly"
154
+ [attr.required]="isRequired ? '' : null"
155
+ [class.error]="hasError"
156
+ [attr.aria-invalid]="hasError ? 'true' : null"
157
+ [attr.aria-describedby]="ariaDescribedBy || null"
158
+ (input)="onInput($event)"
159
+ (blur)="onBlur()"
160
+ />
161
+ </div>
162
+ `, styles: [":host{display:block}.cmp-text-field{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-4)}.label{font-family:var(--body-small-sys-font-family);font-size:var(--body-small-sys-font-size);font-weight:var(--body-small-sys-font-weight);line-height:var(--body-small-sys-line-height);color:var(--surface-surface-sys-on-surface)}.required-marker{color:var(--accent-primary-sys-primary)}.cmp-text-field.disabled .label{opacity:.48}.input{box-sizing:border-box;width:100%;margin:0;border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-sm);background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);font-weight:var(--body-medium-sys-font-weight);line-height:var(--body-medium-sys-line-height);appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s ease,box-shadow .15s ease,background-color .15s ease}.input::placeholder{color:var(--surface-surface-sys-on-surface-variant)}.input:hover:not(:disabled):not(:focus){border-color:var(--outline-sys-outline-variant);box-shadow:0 4px 16px var(--transparent-neutral-sys-black-10),0 2px 4px #00000014}.input:focus{border-color:var(--accent-primary-sys-primary);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input.error:not(:disabled):not(:focus){border-color:var(--error-sys-error)}.input.error:focus{border-color:var(--error-sys-error);border-width:var(--border-width-sys-stroke-medium);box-shadow:0 0 0 4px var(--transparent-primary-sys-primary-08)}.input:disabled{background-color:var(--surface-surface-container-sys-surface-container-highest);color:var(--surface-surface-sys-on-surface-variant);border-color:var(--outline-sys-outline);cursor:not-allowed;opacity:.48}.cmp-text-field.small .input{height:44px;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-12);font-size:var(--body-small-sys-font-size);line-height:var(--body-small-sys-line-height)}.cmp-text-field.medium .input{height:52px;padding:var(--spacing-padding-sys-padding-12) var(--spacing-padding-sys-padding-16);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height)}.cmp-text-field.large .input{height:64px;padding:var(--spacing-padding-sys-padding-20) var(--spacing-padding-sys-padding-16);font-size:var(--body-large-sys-font-size);line-height:var(--body-large-sys-line-height)}@media(forced-colors:active){.input{border:1px solid ButtonText;forced-color-adjust:none}.input:focus{outline:2px solid Highlight;outline-offset:2px}.input:disabled{color:GrayText;border-color:GrayText;background:ButtonFace;opacity:1}.cmp-text-field.disabled .label{color:GrayText;opacity:1}}\n"] }]
163
+ }], ctorParameters: () => [], propDecorators: { label: [{
164
+ type: Input
165
+ }], placeholder: [{
166
+ type: Input
167
+ }], value: [{
168
+ type: Input
169
+ }], type: [{
170
+ type: Input
171
+ }], size: [{
172
+ type: Input
173
+ }], name: [{
174
+ type: Input
175
+ }], id: [{
176
+ type: Input
177
+ }], ariaDescribedBy: [{
178
+ type: Input
179
+ }], required: [{
180
+ type: Input,
181
+ args: [{ transform: booleanAttribute }]
182
+ }], error: [{
183
+ type: Input,
184
+ args: [{ transform: booleanAttribute }]
185
+ }], disabled: [{
186
+ type: Input,
187
+ args: [{ transform: booleanAttribute }]
188
+ }], readonly: [{
189
+ type: Input,
190
+ args: [{ transform: booleanAttribute }]
191
+ }], valueChange: [{
192
+ type: Output
193
+ }] } });
194
+ //# sourceMappingURL=text-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-input.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/text-input/text-input.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,SAAS,EACT,YAAY,EACZ,KAAK,EACL,MAAM,EACN,gBAAgB,EAChB,MAAM,GACP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAwB,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;;AAQ7E,IAAI,MAAM,GAAG,CAAC,CAAC;AAEf;;;;;;;GAOG;AAiCH,MAAM,OAAO,kBAAkB;IACZ,OAAO,GAAG,kBAAkB,EAAE,MAAM,EAAE,CAAC;IACvC,GAAG,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACzC,SAAS,GAA4B,GAAG,EAAE,GAAE,CAAC,CAAC;IAC9C,UAAU,GAAe,GAAG,EAAE,GAAE,CAAC,CAAC;IAE1C,qFAAqF;IAClE,SAAS,CAAmB;IAE/C;QACE,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,IAAI,IAAI;YAAE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QACpC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;IAED,uCAAuC;IAC9B,KAAK,GAAG,EAAE,CAAC;IACpB,0DAA0D;IACjD,WAAW,GAAG,EAAE,CAAC;IAC1B,oFAAoF;IAC3E,KAAK,GAAG,EAAE,CAAC;IACpB,mFAAmF;IAC1E,IAAI,GAAkB,MAAM,CAAC;IACtC,sFAAsF;IAC7E,IAAI,GAAkB,QAAQ,CAAC;IACxC,6FAA6F;IACpF,IAAI,CAAU;IACvB,0EAA0E;IACjE,EAAE,CAAU;IACrB;;;OAGG;IACM,eAAe,CAAU;IAElC;;;;OAIG;IACqC,QAAQ,GAAG,KAAK,CAAC;IACzD;;;;;OAKG;IACqC,KAAK,GAAG,KAAK,CAAC;IACtD,oHAAoH;IAC5E,QAAQ,GAAG,KAAK,CAAC;IACzD,8IAA8I;IACtG,QAAQ,GAAG,KAAK,CAAC;IAEzD,0IAA0I;IACvH,WAAW,GAAG,IAAI,YAAY,EAAU,CAAC;IAE5D,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC;IACjC,CAAC;IAED,iGAAiG;IACjG,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAC7E,CAAC;IAED,qGAAqG;IACrG,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,YAAY,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzF,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,CAAC,gBAAgB,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;aAClE,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;IAED,+EAA+E;IAE/E,UAAU,CAAC,KAAoB;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;IAED,gBAAgB,CAAC,EAA2B;QAC1C,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;IACtB,CAAC;IAED,iBAAiB,CAAC,EAAc;QAC9B,IAAI,CAAC,UAAU,GAAG,EAAE,CAAC;IACvB,CAAC;IAED,4FAA4F;IAC5F,gBAAgB,CAAC,UAAmB;QAClC,IAAI,CAAC,QAAQ,GAAG,UAAU,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;IAED,+EAA+E;IAErE,OAAO,CAAC,KAAY;QAC5B,MAAM,KAAK,GAAI,KAAK,CAAC,MAA2B,CAAC,KAAK,CAAC;QACvD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACtB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAES,MAAM;QACd,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC1B,CAAC;wGA7GU,kBAAkB;4FAAlB,kBAAkB,wPAwCT,gBAAgB,6BAOhB,gBAAgB,sCAEhB,gBAAgB,sCAEhB,gBAAgB,sEA/E1B;;;;;;;;;;;;;;;;;;;;;;;;;GAyBT;;4FAGU,kBAAkB;kBAhC9B,SAAS;+BACE,qBAAqB,cACnB,IAAI,mBACC,uBAAuB,CAAC,MAAM,YACrC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBT;;sBAmBA,KAAK;;sBAEL,KAAK;;sBAEL,KAAK;;sBAEL,KAAK;;sBAEL,KAAK;;sBAEL,KAAK;;sBAEL,KAAK;;sBAKL,KAAK;;sBAOL,KAAK;uBAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE;;sBAOrC,KAAK;uBAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE;;sBAErC,KAAK;uBAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE;;sBAErC,KAAK;uBAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE;;sBAGrC,MAAM","sourcesContent":["import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n Input,\n Output,\n booleanAttribute,\n inject,\n} from '@angular/core';\nimport { ControlValueAccessor, NgControl, Validators } from '@angular/forms';\n\n/** Size variant of a `compsych-text-input`. Controls input height, padding, and font size. */\nexport type TextInputSize = 'small' | 'medium' | 'large';\n\n/** Native input type. */\nexport type TextInputType = 'text' | 'password';\n\nlet nextId = 0;\n\n/**\n * A labelled text input that wraps a native `<input>` element.\n * Implements `ControlValueAccessor` so it works with both reactive forms\n * (`formControlName`, `[formControl]`) and template-driven forms (`ngModel`).\n * The error visual state is driven automatically by the bound `FormControl`'s\n * validity once the field has been touched, and can also be set explicitly via\n * the `error` input for use outside of Angular forms.\n */\n@Component({\n selector: 'compsych-text-input',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div [class]=\"wrapperClasses\">\n <label class=\"label\" [for]=\"inputId\">\n {{ label }}\n @if (isRequired) {\n <span class=\"required-marker\" aria-hidden=\"true\"> *</span>\n }\n </label>\n <input\n class=\"input\"\n [id]=\"inputId\"\n [name]=\"name || inputId\"\n [type]=\"type\"\n [placeholder]=\"placeholder\"\n [value]=\"value\"\n [disabled]=\"disabled\"\n [readOnly]=\"readonly\"\n [attr.required]=\"isRequired ? '' : null\"\n [class.error]=\"hasError\"\n [attr.aria-invalid]=\"hasError ? 'true' : null\"\n [attr.aria-describedby]=\"ariaDescribedBy || null\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\"\n />\n </div>\n `,\n styleUrl: './text-input.css',\n})\nexport class TextInputComponent implements ControlValueAccessor {\n private readonly _autoId = `cmp-text-input-${++nextId}`;\n private readonly cdr = inject(ChangeDetectorRef);\n private _onChange: (value: string) => void = () => {};\n private _onTouched: () => void = () => {};\n\n /** Injected when the component is used inside a reactive or template-driven form. */\n protected readonly ngControl: NgControl | null;\n\n constructor() {\n const ctrl = inject(NgControl, { optional: true, self: true });\n if (ctrl) ctrl.valueAccessor = this;\n this.ngControl = ctrl;\n }\n\n /** Label displayed above the input. */\n @Input() label = '';\n /** Placeholder text shown inside the input when empty. */\n @Input() placeholder = '';\n /** Current value of the input. When used in a form, the form controls the value. */\n @Input() value = '';\n /** Native input type. Defaults to `'text'`. Use `'password'` to mask the value. */\n @Input() type: TextInputType = 'text';\n /** Size variant — controls height, padding, and font size. Defaults to `'medium'`. */\n @Input() size: TextInputSize = 'medium';\n /** Native `name` attribute forwarded to the `<input>`. Defaults to the auto-generated id. */\n @Input() name?: string;\n /** Native `id` attribute for the `<input>`. Auto-generated if omitted. */\n @Input() id?: string;\n /**\n * Id(s) of external elements that describe the input, space-separated.\n * Forwarded to `aria-describedby` on the native `<input>`.\n */\n @Input() ariaDescribedBy?: string;\n\n /**\n * Adds a blue asterisk after the label. When used in a form, this is set\n * automatically if the bound `FormControl` has `Validators.required`.\n * Can also be set explicitly for non-form use. Supports boolean coercion.\n */\n @Input({ transform: booleanAttribute }) required = false;\n /**\n * Applies the error visual state (red border) explicitly. When used in a form,\n * the error state is driven automatically from the `FormControl`'s validity once\n * the field is touched — this input is only needed outside of forms.\n * Supports boolean coercion.\n */\n @Input({ transform: booleanAttribute }) error = false;\n /** Disables the input. When used in a form, also responds to `FormControl.disable()`. Supports boolean coercion. */\n @Input({ transform: booleanAttribute }) disabled = false;\n /** Makes the input read-only — focusable and copyable but not editable. Value is still submitted with the form. Supports boolean coercion. */\n @Input({ transform: booleanAttribute }) readonly = false;\n\n /** Emits the new string value whenever the user types. For use outside Angular forms; inside forms use the `FormControl` value stream. */\n @Output() readonly valueChange = new EventEmitter<string>();\n\n get inputId(): string {\n return this.id ?? this._autoId;\n }\n\n /** True when the bound FormControl is invalid and touched, or when `error` is explicitly set. */\n get hasError(): boolean {\n return this.error || !!(this.ngControl?.invalid && this.ngControl.touched);\n }\n\n /** True when `required` is set explicitly, or when the bound FormControl has Validators.required. */\n get isRequired(): boolean {\n return this.required || !!(this.ngControl?.control?.hasValidator(Validators.required));\n }\n\n get wrapperClasses(): string {\n return ['cmp-text-field', this.size, this.disabled ? 'disabled' : '']\n .filter(Boolean)\n .join(' ');\n }\n\n // ── ControlValueAccessor ────────────────────────────────────────────────────\n\n writeValue(value: string | null): void {\n this.value = value ?? '';\n this.cdr.markForCheck();\n }\n\n registerOnChange(fn: (value: string) => void): void {\n this._onChange = fn;\n }\n\n registerOnTouched(fn: () => void): void {\n this._onTouched = fn;\n }\n\n /** Called by Angular when the bound FormControl is programmatically disabled or enabled. */\n setDisabledState(isDisabled: boolean): void {\n this.disabled = isDisabled;\n this.cdr.markForCheck();\n }\n\n // ── Event handlers ──────────────────────────────────────────────────────────\n\n protected onInput(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n this.value = value;\n this._onChange(value);\n this.valueChange.emit(value);\n }\n\n protected onBlur(): void {\n this._onTouched();\n this.cdr.markForCheck();\n }\n}\n"]}
package/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import * as i4 from "./lib/header-nav/header-nav";
6
6
  import * as i5 from "./lib/sidebar/sidebar";
7
7
  import * as i6 from "./lib/card-base/card";
8
8
  import * as i7 from "./lib/card-grid/card-grid";
9
+ import * as i8 from "./lib/text-input/text-input";
9
10
  export { ButtonComponent } from './lib/button/button';
10
11
  export type { ButtonVariant, ButtonSize, ButtonType } from './lib/button/button';
11
12
  export { AccordionComponent } from './lib/accordion/accordion';
@@ -17,12 +18,14 @@ export type { HeaderNavLevel, NavAction } from './lib/header-nav/header-nav';
17
18
  export { SidebarComponent } from './lib/sidebar/sidebar';
18
19
  export type { SidebarItem } from './lib/sidebar/sidebar';
19
20
  export { CardComponent } from './lib/card-base/card';
20
- export type { CardVariant, CardAppearance, CardRadius, CardPadding, CardOrientation } from './lib/card-base/card';
21
+ export type { CardVariant, CardAppearance, CardRadius, CardPadding, CardOrientation, CardHeadingLevel } from './lib/card-base/card';
21
22
  export { RippleDirective } from './lib/card-base/ripple.directive';
22
23
  export { CardGridComponent } from './lib/card-grid/card-grid';
23
24
  export type { CardGridLayout } from './lib/card-grid/card-grid';
25
+ export { TextInputComponent } from './lib/text-input/text-input';
26
+ export type { TextInputSize, TextInputType } from './lib/text-input/text-input';
24
27
  export declare class CompsychAngularModule {
25
28
  static ɵfac: i0.ɵɵFactoryDeclaration<CompsychAngularModule, never>;
26
- static ɵmod: i0.ɵɵNgModuleDeclaration<CompsychAngularModule, never, [typeof i1.ButtonComponent, typeof i2.AccordionComponent, typeof i3.FooterComponent, typeof i4.HeaderNavComponent, typeof i5.SidebarComponent, typeof i6.CardComponent, typeof i7.CardGridComponent], [typeof i1.ButtonComponent, typeof i2.AccordionComponent, typeof i3.FooterComponent, typeof i4.HeaderNavComponent, typeof i5.SidebarComponent, typeof i6.CardComponent, typeof i7.CardGridComponent]>;
29
+ static ɵmod: i0.ɵɵNgModuleDeclaration<CompsychAngularModule, never, [typeof i1.ButtonComponent, typeof i2.AccordionComponent, typeof i3.FooterComponent, typeof i4.HeaderNavComponent, typeof i5.SidebarComponent, typeof i6.CardComponent, typeof i7.CardGridComponent, typeof i8.TextInputComponent], [typeof i1.ButtonComponent, typeof i2.AccordionComponent, typeof i3.FooterComponent, typeof i4.HeaderNavComponent, typeof i5.SidebarComponent, typeof i6.CardComponent, typeof i7.CardGridComponent, typeof i8.TextInputComponent]>;
27
30
  static ɵinj: i0.ɵɵInjectorDeclaration<CompsychAngularModule>;
28
31
  }
@@ -11,6 +11,8 @@ export type CardRadius = 'md' | 'lg' | 'xl';
11
11
  export type CardPadding = 'none' | 'sm' | 'md' | 'lg' | 'xl';
12
12
  /** Action-card layout. */
13
13
  export type CardOrientation = 'horizontal' | 'vertical';
14
+ /** Heading level for the title / hero headline / action label (screen-reader heading semantics). */
15
+ export type CardHeadingLevel = 2 | 3 | 4 | 5 | 6;
14
16
  /**
15
17
  * Card — the single Cards component for the ComPsych Design System.
16
18
  *
@@ -75,6 +77,14 @@ export declare class CardComponent {
75
77
  readonly value: import("@angular/core").InputSignal<string | number>;
76
78
  /** Metric: selected/current state (primary border + text). */
77
79
  readonly current: import("@angular/core").InputSignal<boolean>;
80
+ /**
81
+ * Opts the title / hero headline / action label into a heading in the
82
+ * accessibility tree via `role="heading"` + `aria-level`, so consumers that
83
+ * replace real headings keep heading semantics and screen-reader heading
84
+ * navigation (WCAG 1.3.1). Omit (default) to render as plain text — no visual
85
+ * or behavioral change for existing usages.
86
+ */
87
+ readonly headingLevel: import("@angular/core").InputSignal<CardHeadingLevel | undefined>;
78
88
  /** Emits on click / Enter / Space when interactive and not disabled. */
79
89
  readonly cardClick: import("@angular/core").OutputEmitterRef<void>;
80
90
  private readonly sanitizer;
@@ -90,5 +100,5 @@ export declare class CardComponent {
90
100
  /** Renders a sized/weighted Lucide SVG for template use. */
91
101
  iconSvg(name: string, size: number, strokeWidth?: number): SafeHtml;
92
102
  static ɵfac: i0.ɵɵFactoryDeclaration<CardComponent, never>;
93
- static ɵcmp: i0.ɵɵComponentDeclaration<CardComponent, "compsych-card", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "appearance": { "alias": "appearance"; "required": false; "isSignal": true; }; "radius": { "alias": "radius"; "required": false; "isSignal": true; }; "padding": { "alias": "padding"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "imageSrc": { "alias": "imageSrc"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "plainIcon": { "alias": "plainIcon"; "required": false; "isSignal": true; }; "category": { "alias": "category"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "headline": { "alias": "headline"; "required": false; "isSignal": true; }; "actionLabel": { "alias": "actionLabel"; "required": false; "isSignal": true; }; "showArrow": { "alias": "showArrow"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "trailingIcon": { "alias": "trailingIcon"; "required": false; "isSignal": true; }; "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "current": { "alias": "current"; "required": false; "isSignal": true; }; }, { "cardClick": "cardClick"; }, never, ["*"], true, [{ directive: typeof i1.RippleDirective; inputs: {}; outputs: {}; }]>;
103
+ static ɵcmp: i0.ɵɵComponentDeclaration<CardComponent, "compsych-card", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "appearance": { "alias": "appearance"; "required": false; "isSignal": true; }; "radius": { "alias": "radius"; "required": false; "isSignal": true; }; "padding": { "alias": "padding"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "imageSrc": { "alias": "imageSrc"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "plainIcon": { "alias": "plainIcon"; "required": false; "isSignal": true; }; "category": { "alias": "category"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "headline": { "alias": "headline"; "required": false; "isSignal": true; }; "actionLabel": { "alias": "actionLabel"; "required": false; "isSignal": true; }; "showArrow": { "alias": "showArrow"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "trailingIcon": { "alias": "trailingIcon"; "required": false; "isSignal": true; }; "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "current": { "alias": "current"; "required": false; "isSignal": true; }; "headingLevel": { "alias": "headingLevel"; "required": false; "isSignal": true; }; }, { "cardClick": "cardClick"; }, never, ["*"], true, [{ directive: typeof i1.RippleDirective; inputs: {}; outputs: {}; }]>;
94
104
  }
@@ -0,0 +1,81 @@
1
+ import { EventEmitter } from '@angular/core';
2
+ import { ControlValueAccessor, NgControl } from '@angular/forms';
3
+ import * as i0 from "@angular/core";
4
+ /** Size variant of a `compsych-text-input`. Controls input height, padding, and font size. */
5
+ export type TextInputSize = 'small' | 'medium' | 'large';
6
+ /** Native input type. */
7
+ export type TextInputType = 'text' | 'password';
8
+ /**
9
+ * A labelled text input that wraps a native `<input>` element.
10
+ * Implements `ControlValueAccessor` so it works with both reactive forms
11
+ * (`formControlName`, `[formControl]`) and template-driven forms (`ngModel`).
12
+ * The error visual state is driven automatically by the bound `FormControl`'s
13
+ * validity once the field has been touched, and can also be set explicitly via
14
+ * the `error` input for use outside of Angular forms.
15
+ */
16
+ export declare class TextInputComponent implements ControlValueAccessor {
17
+ private readonly _autoId;
18
+ private readonly cdr;
19
+ private _onChange;
20
+ private _onTouched;
21
+ /** Injected when the component is used inside a reactive or template-driven form. */
22
+ protected readonly ngControl: NgControl | null;
23
+ constructor();
24
+ /** Label displayed above the input. */
25
+ label: string;
26
+ /** Placeholder text shown inside the input when empty. */
27
+ placeholder: string;
28
+ /** Current value of the input. When used in a form, the form controls the value. */
29
+ value: string;
30
+ /** Native input type. Defaults to `'text'`. Use `'password'` to mask the value. */
31
+ type: TextInputType;
32
+ /** Size variant — controls height, padding, and font size. Defaults to `'medium'`. */
33
+ size: TextInputSize;
34
+ /** Native `name` attribute forwarded to the `<input>`. Defaults to the auto-generated id. */
35
+ name?: string;
36
+ /** Native `id` attribute for the `<input>`. Auto-generated if omitted. */
37
+ id?: string;
38
+ /**
39
+ * Id(s) of external elements that describe the input, space-separated.
40
+ * Forwarded to `aria-describedby` on the native `<input>`.
41
+ */
42
+ ariaDescribedBy?: string;
43
+ /**
44
+ * Adds a blue asterisk after the label. When used in a form, this is set
45
+ * automatically if the bound `FormControl` has `Validators.required`.
46
+ * Can also be set explicitly for non-form use. Supports boolean coercion.
47
+ */
48
+ required: boolean;
49
+ /**
50
+ * Applies the error visual state (red border) explicitly. When used in a form,
51
+ * the error state is driven automatically from the `FormControl`'s validity once
52
+ * the field is touched — this input is only needed outside of forms.
53
+ * Supports boolean coercion.
54
+ */
55
+ error: boolean;
56
+ /** Disables the input. When used in a form, also responds to `FormControl.disable()`. Supports boolean coercion. */
57
+ disabled: boolean;
58
+ /** Makes the input read-only — focusable and copyable but not editable. Value is still submitted with the form. Supports boolean coercion. */
59
+ readonly: boolean;
60
+ /** Emits the new string value whenever the user types. For use outside Angular forms; inside forms use the `FormControl` value stream. */
61
+ readonly valueChange: EventEmitter<string>;
62
+ get inputId(): string;
63
+ /** True when the bound FormControl is invalid and touched, or when `error` is explicitly set. */
64
+ get hasError(): boolean;
65
+ /** True when `required` is set explicitly, or when the bound FormControl has Validators.required. */
66
+ get isRequired(): boolean;
67
+ get wrapperClasses(): string;
68
+ writeValue(value: string | null): void;
69
+ registerOnChange(fn: (value: string) => void): void;
70
+ registerOnTouched(fn: () => void): void;
71
+ /** Called by Angular when the bound FormControl is programmatically disabled or enabled. */
72
+ setDisabledState(isDisabled: boolean): void;
73
+ protected onInput(event: Event): void;
74
+ protected onBlur(): void;
75
+ static ɵfac: i0.ɵɵFactoryDeclaration<TextInputComponent, never>;
76
+ static ɵcmp: i0.ɵɵComponentDeclaration<TextInputComponent, "compsych-text-input", never, { "label": { "alias": "label"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "value": { "alias": "value"; "required": false; }; "type": { "alias": "type"; "required": false; }; "size": { "alias": "size"; "required": false; }; "name": { "alias": "name"; "required": false; }; "id": { "alias": "id"; "required": false; }; "ariaDescribedBy": { "alias": "ariaDescribedBy"; "required": false; }; "required": { "alias": "required"; "required": false; }; "error": { "alias": "error"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "readonly": { "alias": "readonly"; "required": false; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
77
+ static ngAcceptInputType_required: unknown;
78
+ static ngAcceptInputType_error: unknown;
79
+ static ngAcceptInputType_disabled: unknown;
80
+ static ngAcceptInputType_readonly: unknown;
81
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compsych-ui-components/angular",
3
- "version": "7.0.5",
3
+ "version": "7.0.9",
4
4
  "description": "Angular UI components for compsych",
5
5
  "publishConfig": {
6
6
  "access": "public",