@compsych-ui-components/angular 4.0.19 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm2022/index.js CHANGED
@@ -1,21 +1,23 @@
1
1
  import { NgModule } from '@angular/core';
2
2
  import { ButtonComponent } from './lib/button/button';
3
3
  import { AccordionComponent } from './lib/accordion/accordion';
4
- import { AccordionItemComponent } from './lib/accordion/accordion-item';
4
+ import { HeaderNavComponent } from './lib/header-nav/header-nav';
5
+ import { SidebarComponent } from './lib/sidebar/sidebar';
5
6
  import * as i0 from "@angular/core";
6
7
  export { ButtonComponent } from './lib/button/button';
7
8
  export { AccordionComponent } from './lib/accordion/accordion';
8
- export { AccordionItemComponent } from './lib/accordion/accordion-item';
9
+ export { HeaderNavComponent } from './lib/header-nav/header-nav';
10
+ export { SidebarComponent } from './lib/sidebar/sidebar';
9
11
  export class CompsychAngularModule {
10
12
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
11
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule, imports: [ButtonComponent, AccordionComponent, AccordionItemComponent], exports: [ButtonComponent, AccordionComponent, AccordionItemComponent] });
13
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule, imports: [ButtonComponent, AccordionComponent, HeaderNavComponent, SidebarComponent], exports: [ButtonComponent, AccordionComponent, HeaderNavComponent, SidebarComponent] });
12
14
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule });
13
15
  }
14
16
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule, decorators: [{
15
17
  type: NgModule,
16
18
  args: [{
17
- imports: [ButtonComponent, AccordionComponent, AccordionItemComponent],
18
- exports: [ButtonComponent, AccordionComponent, AccordionItemComponent],
19
+ imports: [ButtonComponent, AccordionComponent, HeaderNavComponent, SidebarComponent],
20
+ exports: [ButtonComponent, AccordionComponent, HeaderNavComponent, SidebarComponent],
19
21
  }]
20
22
  }] });
21
23
  //# sourceMappingURL=index.js.map
@@ -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,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;;AAExE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAMxE,MAAM,OAAO,qBAAqB;wGAArB,qBAAqB;yGAArB,qBAAqB,YAHtB,eAAe,EAAE,kBAAkB,EAAE,sBAAsB,aAC3D,eAAe,EAAE,kBAAkB,EAAE,sBAAsB;yGAE1D,qBAAqB;;4FAArB,qBAAqB;kBAJjC,QAAQ;mBAAC;oBACR,OAAO,EAAE,CAAC,eAAe,EAAE,kBAAkB,EAAE,sBAAsB,CAAC;oBACtE,OAAO,EAAE,CAAC,eAAe,EAAE,kBAAkB,EAAE,sBAAsB,CAAC;iBACvE","sourcesContent":["import { NgModule } from '@angular/core';\r\nimport { ButtonComponent } from './lib/button/button';\r\nimport { AccordionComponent } from './lib/accordion/accordion';\r\nimport { AccordionItemComponent } from './lib/accordion/accordion-item';\r\n\r\nexport { ButtonComponent } from './lib/button/button';\r\nexport { AccordionComponent } from './lib/accordion/accordion';\r\nexport { AccordionItemComponent } from './lib/accordion/accordion-item';\r\n\r\n@NgModule({\r\n imports: [ButtonComponent, AccordionComponent, AccordionItemComponent],\r\n exports: [ButtonComponent, AccordionComponent, AccordionItemComponent],\r\n})\r\nexport class CompsychAngularModule {}\r\n\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,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;;AAEzD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAOzD,MAAM,OAAO,qBAAqB;wGAArB,qBAAqB;yGAArB,qBAAqB,YAHtB,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,aACzE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB;yGAExE,qBAAqB;;4FAArB,qBAAqB;kBAJjC,QAAQ;mBAAC;oBACR,OAAO,EAAE,CAAC,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,CAAC;oBACpF,OAAO,EAAE,CAAC,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,CAAC;iBACrF","sourcesContent":["import { NgModule } from '@angular/core';\nimport { ButtonComponent } from './lib/button/button';\nimport { AccordionComponent } from './lib/accordion/accordion';\nimport { HeaderNavComponent } from './lib/header-nav/header-nav';\nimport { SidebarComponent } from './lib/sidebar/sidebar';\n\nexport { ButtonComponent } from './lib/button/button';\nexport type { ButtonVariant } from './lib/button/button';\nexport { AccordionComponent } from './lib/accordion/accordion';\nexport type { AccordionItem, AccordionButton, AccordionVariant, NestedAccordionItem } from './lib/accordion/accordion-types';\nexport { HeaderNavComponent } from './lib/header-nav/header-nav';\nexport type { HeaderNavLevel, NavAction } from './lib/header-nav/header-nav';\nexport { SidebarComponent } from './lib/sidebar/sidebar';\nexport type { SidebarItem } from './lib/sidebar/sidebar';\n\n@NgModule({\n imports: [ButtonComponent, AccordionComponent, HeaderNavComponent, SidebarComponent],\n exports: [ButtonComponent, AccordionComponent, HeaderNavComponent, SidebarComponent],\n})\nexport class CompsychAngularModule {}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=accordion-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accordion-types.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/accordion/accordion-types.ts"],"names":[],"mappings":"","sourcesContent":["export type AccordionVariant = 'default' | 'level-2' | 'text-only';\n\nexport interface AccordionButton {\n label: string;\n action: () => void;\n}\n\nexport type NestedAccordionItem = Omit<AccordionItem, 'buttons' | 'nestedContent'>;\n\nexport interface AccordionItem {\n title: string;\n content?: string;\n icon?: string;\n expanded?: boolean;\n buttons?: AccordionButton[];\n nestedContent?: NestedAccordionItem[];\n}\n"]}
@@ -1,46 +1,240 @@
1
- import { Component, ContentChildren, Input, } from '@angular/core';
1
+ import { Component, EventEmitter, forwardRef, HostListener, Input, Output, SecurityContext, } from '@angular/core';
2
2
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
3
- import { AccordionItemComponent } from './accordion-item';
3
+ import { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';
4
+ import { ButtonComponent } from '../button/button';
4
5
  import * as i0 from "@angular/core";
6
+ import * as i1 from "@angular/platform-browser";
7
+ let nextId = 0;
8
+ // AccordionItemComponent is defined before AccordionComponent because AccordionComponent
9
+ // uses it directly in its imports. AccordionItemComponent references AccordionComponent
10
+ // via forwardRef to break the forward-reference cycle within this file.
11
+ export class AccordionItemComponent {
12
+ sanitizer;
13
+ el;
14
+ triggerId = `accordion-trigger-${nextId}`;
15
+ panelId = `accordion-panel-${nextId++}`;
16
+ title = '';
17
+ content = '';
18
+ buttons = [];
19
+ nestedContent = [];
20
+ variant = 'default';
21
+ nested = false;
22
+ set icon(name) {
23
+ const svg = LUCIDE_ICONS[name];
24
+ this.iconSvg = svg ? this.sanitizer.bypassSecurityTrustHtml(svg) : null;
25
+ }
26
+ iconSvg = null;
27
+ get sanitizedContent() {
28
+ return this.sanitizer.sanitize(SecurityContext.HTML, this.content) ?? '';
29
+ }
30
+ constructor(sanitizer, el) {
31
+ this.sanitizer = sanitizer;
32
+ this.el = el;
33
+ }
34
+ get expanded() { return this._expanded; }
35
+ set expanded(value) { this._expanded = coerceBooleanProperty(value); }
36
+ _expanded = false;
37
+ expandedChange = new EventEmitter();
38
+ toggle() {
39
+ this._expanded = !this._expanded;
40
+ this.expandedChange.emit(this._expanded);
41
+ }
42
+ onKeydown(event) {
43
+ const target = event.target;
44
+ if (!(target instanceof HTMLButtonElement) || !target.classList.contains('trigger'))
45
+ return;
46
+ const parent = this.el.nativeElement.parentElement;
47
+ if (!parent)
48
+ return;
49
+ const triggers = Array.from(parent.querySelectorAll(':scope > compsych-accordion-item > button.trigger'));
50
+ const currentIndex = triggers.indexOf(target);
51
+ let focusIndex = -1;
52
+ switch (event.key) {
53
+ case 'ArrowDown':
54
+ focusIndex = currentIndex + 1;
55
+ break;
56
+ case 'ArrowUp':
57
+ focusIndex = currentIndex - 1;
58
+ break;
59
+ case 'Home':
60
+ focusIndex = 0;
61
+ break;
62
+ case 'End':
63
+ focusIndex = triggers.length - 1;
64
+ break;
65
+ default: return;
66
+ }
67
+ if (focusIndex >= 0 && focusIndex < triggers.length) {
68
+ triggers[focusIndex].focus();
69
+ event.preventDefault();
70
+ }
71
+ }
72
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: AccordionItemComponent, deps: [{ token: i1.DomSanitizer }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
73
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: AccordionItemComponent, isStandalone: true, selector: "compsych-accordion-item", inputs: { title: "title", content: "content", buttons: "buttons", nestedContent: "nestedContent", variant: "variant", nested: "nested", icon: "icon", expanded: "expanded" }, outputs: { expandedChange: "expandedChange" }, host: { listeners: { "keydown": "onKeydown($event)" }, properties: { "class.level-2": "variant === \"level-2\"", "class.text-only": "variant === \"text-only\"", "class.nested": "nested" }, classAttribute: "compsych-accordion-item" }, ngImport: i0, template: `
74
+ <button
75
+ class="trigger"
76
+ type="button"
77
+ [id]="triggerId"
78
+ [attr.aria-expanded]="expanded"
79
+ [attr.aria-controls]="panelId"
80
+ (click)="toggle()"
81
+ >
82
+ @if (iconSvg) {
83
+ <span class="trigger-leading-icon" [innerHTML]="iconSvg" aria-hidden="true"></span>
84
+ }
85
+ <span class="trigger-label">{{ title }}</span>
86
+ <span class="trigger-toggle material-symbols-rounded" aria-hidden="true">{{ expanded ? 'remove' : 'add' }}</span>
87
+ </button>
88
+ <div
89
+ [id]="panelId"
90
+ role="region"
91
+ [attr.aria-labelledby]="triggerId"
92
+ [hidden]="!expanded"
93
+ >
94
+ @if (content) {
95
+ <div class="content" [innerHTML]="sanitizedContent"></div>
96
+ }
97
+ @if (nestedContent.length) {
98
+ <compsych-accordion [items]="nestedContent" variant="level-2" [nested]="true" />
99
+ }
100
+ @if (buttons.length) {
101
+ <div class="actions">
102
+ @for (btn of buttons; track btn.label) {
103
+ <compsych-button [label]="btn.label" variant="filled" size="small" (click)="btn.action()" />
104
+ }
105
+ </div>
106
+ }
107
+ </div>
108
+ `, isInline: true, styles: [":host{display:block;border-bottom:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant)}:host:first-child{border-top:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant)}.trigger{display:flex;align-items:center;gap:var(--spacing-padding-sys-padding-8);width:100%;padding:var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-24);background:none;border:none;cursor:pointer;text-align:left;color:var(--surface-surface-sys-on-surface);font-family:var(--font-core-title);font-size:var(--size-core-lg);font-weight:var(--label-large-sys-font-weight);line-height:var(--size-core-2xl);letter-spacing:var(--spacing-core-spacing-0);position:relative;overflow:hidden;border-radius:0}.trigger:before{content:\"\";position:absolute;inset:0;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}.trigger:hover:before{opacity:.08}.trigger:active:before{opacity:.1}.trigger-label{flex:1;min-width:0}.trigger-leading-icon{display:flex;align-items:center;justify-content:center;width:var(--sys-size-sm);height:var(--sys-size-sm);color:var(--surface-surface-sys-on-surface);flex-shrink:0}.trigger-leading-icon svg{width:100%;height:100%}.trigger-toggle{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:1.5rem;height:1.5rem;font-size:1rem;line-height:1;color:var(--surface-surface-sys-on-surface-variant);background-color:var(--surface-surface-container-sys-surface-container);border-radius:var(--border-radius-sys-radius-full);box-sizing:border-box}.trigger[aria-expanded=true] .trigger-toggle{color:var(--add-on-primary-fixed-sys-on-primary-fixed);background-color:var(--add-on-primary-fixed-sys-primary-fixed-dim)}.actions{display:flex;flex-wrap:wrap;gap:var(--spacing-padding-sys-padding-12);align-items:center;justify-content:flex-end;padding:var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-24);border-top:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host(.level-2){border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant);border-radius:0;overflow:hidden}:host(.level-2):first-child{border-radius:var(--border-radius-sys-radius-sm) var(--border-radius-sys-radius-sm) 0 0}:host(.level-2):last-child{border-radius:0 0 var(--border-radius-sys-radius-sm) var(--border-radius-sys-radius-sm)}:host(.level-2):only-child{border-radius:var(--border-radius-sys-radius-sm)}:host(.level-2.nested){border-radius:var(--border-radius-sys-radius-sm)}:host(.level-2) .trigger{padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-16);gap:var(--spacing-padding-sys-padding-4);font-size:var(--size-core-base);line-height:var(--size-core-xl)}:host(.level-2) .actions{padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24)}:host(.level-2) .content{padding:0 var(--spacing-padding-sys-padding-24) var(--spacing-padding-sys-padding-8)}:host(.text-only){border:none}:host(.text-only) .trigger{padding:var(--spacing-padding-sys-padding-8) 0;font-size:var(--size-core-base);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--size-core-xl)}:host(.text-only) .trigger-leading-icon{display:none}:host(.text-only) .trigger-toggle{order:-1;color:var(--surface-surface-sys-on-surface-variant);background-color:var(--surface-surface-container-sys-surface-container)}.content{padding:0 var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-16);color:var(--surface-surface-sys-on-surface-variant);font-family:var(--font-core-body);font-size:var(--size-core-base);font-weight:var(--label-large-sys-font-weight);line-height:var(--size-core-xl);letter-spacing:var(--spacing-core-spacing-0)}\n"], dependencies: [{ kind: "component", type: i0.forwardRef(() => ButtonComponent), selector: "compsych-button", inputs: ["label", "variant", "size", "icon", "iconOnly", "disabled"] }, { kind: "component", type: i0.forwardRef(() => AccordionComponent), selector: "compsych-accordion", inputs: ["items", "variant", "nested", "multi"] }] });
109
+ }
110
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: AccordionItemComponent, decorators: [{
111
+ type: Component,
112
+ args: [{ selector: 'compsych-accordion-item', standalone: true, imports: [ButtonComponent, forwardRef(() => AccordionComponent)], template: `
113
+ <button
114
+ class="trigger"
115
+ type="button"
116
+ [id]="triggerId"
117
+ [attr.aria-expanded]="expanded"
118
+ [attr.aria-controls]="panelId"
119
+ (click)="toggle()"
120
+ >
121
+ @if (iconSvg) {
122
+ <span class="trigger-leading-icon" [innerHTML]="iconSvg" aria-hidden="true"></span>
123
+ }
124
+ <span class="trigger-label">{{ title }}</span>
125
+ <span class="trigger-toggle material-symbols-rounded" aria-hidden="true">{{ expanded ? 'remove' : 'add' }}</span>
126
+ </button>
127
+ <div
128
+ [id]="panelId"
129
+ role="region"
130
+ [attr.aria-labelledby]="triggerId"
131
+ [hidden]="!expanded"
132
+ >
133
+ @if (content) {
134
+ <div class="content" [innerHTML]="sanitizedContent"></div>
135
+ }
136
+ @if (nestedContent.length) {
137
+ <compsych-accordion [items]="nestedContent" variant="level-2" [nested]="true" />
138
+ }
139
+ @if (buttons.length) {
140
+ <div class="actions">
141
+ @for (btn of buttons; track btn.label) {
142
+ <compsych-button [label]="btn.label" variant="filled" size="small" (click)="btn.action()" />
143
+ }
144
+ </div>
145
+ }
146
+ </div>
147
+ `, host: {
148
+ class: 'compsych-accordion-item',
149
+ '[class.level-2]': 'variant === "level-2"',
150
+ '[class.text-only]': 'variant === "text-only"',
151
+ '[class.nested]': 'nested',
152
+ }, styles: [":host{display:block;border-bottom:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant)}:host:first-child{border-top:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant)}.trigger{display:flex;align-items:center;gap:var(--spacing-padding-sys-padding-8);width:100%;padding:var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-24);background:none;border:none;cursor:pointer;text-align:left;color:var(--surface-surface-sys-on-surface);font-family:var(--font-core-title);font-size:var(--size-core-lg);font-weight:var(--label-large-sys-font-weight);line-height:var(--size-core-2xl);letter-spacing:var(--spacing-core-spacing-0);position:relative;overflow:hidden;border-radius:0}.trigger:before{content:\"\";position:absolute;inset:0;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}.trigger:hover:before{opacity:.08}.trigger:active:before{opacity:.1}.trigger-label{flex:1;min-width:0}.trigger-leading-icon{display:flex;align-items:center;justify-content:center;width:var(--sys-size-sm);height:var(--sys-size-sm);color:var(--surface-surface-sys-on-surface);flex-shrink:0}.trigger-leading-icon svg{width:100%;height:100%}.trigger-toggle{display:flex;align-items:center;justify-content:center;flex-shrink:0;width:1.5rem;height:1.5rem;font-size:1rem;line-height:1;color:var(--surface-surface-sys-on-surface-variant);background-color:var(--surface-surface-container-sys-surface-container);border-radius:var(--border-radius-sys-radius-full);box-sizing:border-box}.trigger[aria-expanded=true] .trigger-toggle{color:var(--add-on-primary-fixed-sys-on-primary-fixed);background-color:var(--add-on-primary-fixed-sys-primary-fixed-dim)}.actions{display:flex;flex-wrap:wrap;gap:var(--spacing-padding-sys-padding-12);align-items:center;justify-content:flex-end;padding:var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-24);border-top:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host(.level-2){border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline-variant);border-radius:0;overflow:hidden}:host(.level-2):first-child{border-radius:var(--border-radius-sys-radius-sm) var(--border-radius-sys-radius-sm) 0 0}:host(.level-2):last-child{border-radius:0 0 var(--border-radius-sys-radius-sm) var(--border-radius-sys-radius-sm)}:host(.level-2):only-child{border-radius:var(--border-radius-sys-radius-sm)}:host(.level-2.nested){border-radius:var(--border-radius-sys-radius-sm)}:host(.level-2) .trigger{padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-16);gap:var(--spacing-padding-sys-padding-4);font-size:var(--size-core-base);line-height:var(--size-core-xl)}:host(.level-2) .actions{padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24)}:host(.level-2) .content{padding:0 var(--spacing-padding-sys-padding-24) var(--spacing-padding-sys-padding-8)}:host(.text-only){border:none}:host(.text-only) .trigger{padding:var(--spacing-padding-sys-padding-8) 0;font-size:var(--size-core-base);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--size-core-xl)}:host(.text-only) .trigger-leading-icon{display:none}:host(.text-only) .trigger-toggle{order:-1;color:var(--surface-surface-sys-on-surface-variant);background-color:var(--surface-surface-container-sys-surface-container)}.content{padding:0 var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-16);color:var(--surface-surface-sys-on-surface-variant);font-family:var(--font-core-body);font-size:var(--size-core-base);font-weight:var(--label-large-sys-font-weight);line-height:var(--size-core-xl);letter-spacing:var(--spacing-core-spacing-0)}\n"] }]
153
+ }], ctorParameters: () => [{ type: i1.DomSanitizer }, { type: i0.ElementRef }], propDecorators: { title: [{
154
+ type: Input
155
+ }], content: [{
156
+ type: Input
157
+ }], buttons: [{
158
+ type: Input
159
+ }], nestedContent: [{
160
+ type: Input
161
+ }], variant: [{
162
+ type: Input
163
+ }], nested: [{
164
+ type: Input
165
+ }], icon: [{
166
+ type: Input
167
+ }], expanded: [{
168
+ type: Input
169
+ }], expandedChange: [{
170
+ type: Output
171
+ }], onKeydown: [{
172
+ type: HostListener,
173
+ args: ['keydown', ['$event']]
174
+ }] } });
5
175
  export class AccordionComponent {
6
- items;
176
+ items = [];
177
+ variant = 'default';
178
+ nested = false;
7
179
  get multi() { return this._multi; }
8
180
  set multi(value) { this._multi = coerceBooleanProperty(value); }
9
181
  _multi = false;
10
- subscriptions = [];
11
- ngAfterContentInit() {
12
- this.wireItems();
13
- this.items.changes.subscribe(() => {
14
- this.unwireItems();
15
- this.wireItems();
16
- });
17
- }
18
- ngOnDestroy() {
19
- this.unwireItems();
20
- }
21
- wireItems() {
22
- this.items.forEach(item => {
23
- const sub = item.expandedChange.subscribe(isExpanded => {
24
- if (isExpanded && !this._multi) {
25
- this.items.filter(i => i !== item).forEach(i => (i.expanded = false));
26
- }
27
- });
28
- this.subscriptions.push(sub);
29
- });
30
- }
31
- unwireItems() {
32
- this.subscriptions.forEach(s => s.unsubscribe());
33
- this.subscriptions = [];
182
+ expandedSet = new Set();
183
+ onExpandedChange(index, isExpanded) {
184
+ if (isExpanded) {
185
+ if (!this._multi)
186
+ this.expandedSet.clear();
187
+ this.expandedSet.add(index);
188
+ }
189
+ else {
190
+ this.expandedSet.delete(index);
191
+ }
192
+ this.expandedSet = new Set(this.expandedSet);
34
193
  }
35
194
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
36
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: AccordionComponent, isStandalone: true, selector: "compsych-accordion", inputs: { multi: "multi" }, queries: [{ propertyName: "items", predicate: AccordionItemComponent }], ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:block;background-color:var(--sys-surface-container-lowest);border:var(--sys-stroke-thin) solid var(--sys-outline);border-radius:var(--sys-radius-md);overflow:hidden}\n"] });
195
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: AccordionComponent, isStandalone: true, selector: "compsych-accordion", inputs: { items: "items", variant: "variant", nested: "nested", multi: "multi" }, host: { properties: { "class.level-2": "variant === \"level-2\"", "class.text-only": "variant === \"text-only\"", "class.nested": "nested" } }, ngImport: i0, template: `
196
+ @for (item of items; track $index; let i = $index) {
197
+ <compsych-accordion-item
198
+ [title]="item.title"
199
+ [content]="item.content ?? ''"
200
+ [icon]="item.icon ?? ''"
201
+ [buttons]="item.buttons ?? []"
202
+ [nestedContent]="item.nestedContent ?? []"
203
+ [variant]="variant"
204
+ [nested]="nested"
205
+ [expanded]="expandedSet.has(i)"
206
+ (expandedChange)="onExpandedChange(i, $event)"
207
+ />
208
+ }
209
+ `, isInline: true, styles: [":host{display:block;background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-md);overflow:hidden}:host(.level-2){display:flex;flex-direction:column;background-color:transparent;border:none;border-radius:0;overflow:visible}:host(.level-2.nested){gap:var(--spacing-padding-sys-padding-12);padding:0 var(--spacing-padding-sys-padding-24) var(--spacing-padding-sys-padding-24)}:host(.text-only){background-color:transparent;border:none;border-radius:0;overflow:visible}\n"], dependencies: [{ kind: "component", type: AccordionItemComponent, selector: "compsych-accordion-item", inputs: ["title", "content", "buttons", "nestedContent", "variant", "nested", "icon", "expanded"], outputs: ["expandedChange"] }] });
37
210
  }
38
211
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: AccordionComponent, decorators: [{
39
212
  type: Component,
40
- args: [{ selector: 'compsych-accordion', standalone: true, imports: [AccordionItemComponent], template: `<ng-content />`, styles: [":host{display:block;background-color:var(--sys-surface-container-lowest);border:var(--sys-stroke-thin) solid var(--sys-outline);border-radius:var(--sys-radius-md);overflow:hidden}\n"] }]
213
+ args: [{ selector: 'compsych-accordion', standalone: true, imports: [AccordionItemComponent], template: `
214
+ @for (item of items; track $index; let i = $index) {
215
+ <compsych-accordion-item
216
+ [title]="item.title"
217
+ [content]="item.content ?? ''"
218
+ [icon]="item.icon ?? ''"
219
+ [buttons]="item.buttons ?? []"
220
+ [nestedContent]="item.nestedContent ?? []"
221
+ [variant]="variant"
222
+ [nested]="nested"
223
+ [expanded]="expandedSet.has(i)"
224
+ (expandedChange)="onExpandedChange(i, $event)"
225
+ />
226
+ }
227
+ `, host: {
228
+ '[class.level-2]': 'variant === "level-2"',
229
+ '[class.text-only]': 'variant === "text-only"',
230
+ '[class.nested]': 'nested',
231
+ }, styles: [":host{display:block;background-color:var(--surface-surface-container-sys-surface-container-lowest);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-md);overflow:hidden}:host(.level-2){display:flex;flex-direction:column;background-color:transparent;border:none;border-radius:0;overflow:visible}:host(.level-2.nested){gap:var(--spacing-padding-sys-padding-12);padding:0 var(--spacing-padding-sys-padding-24) var(--spacing-padding-sys-padding-24)}:host(.text-only){background-color:transparent;border:none;border-radius:0;overflow:visible}\n"] }]
41
232
  }], propDecorators: { items: [{
42
- type: ContentChildren,
43
- args: [AccordionItemComponent]
233
+ type: Input
234
+ }], variant: [{
235
+ type: Input
236
+ }], nested: [{
237
+ type: Input
44
238
  }], multi: [{
45
239
  type: Input
46
240
  }] } });
@@ -1 +1 @@
1
- {"version":3,"file":"accordion.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/accordion/accordion.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,SAAS,EACT,eAAe,EACf,KAAK,GAGN,MAAM,eAAe,CAAC;AACvB,OAAO,EAAgB,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;;AAS1D,MAAM,OAAO,kBAAkB;IACY,KAAK,CAAqC;IAEnF,IACI,KAAK,KAAc,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5C,IAAI,KAAK,CAAC,KAAmB,IAAI,IAAI,CAAC,MAAM,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,GAAG,KAAK,CAAC;IAEf,aAAa,GAA8B,EAAE,CAAC;IAEtD,kBAAkB;QAChB,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE;YAChC,IAAI,CAAC,WAAW,EAAE,CAAC;YACnB,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;YACxB,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE;gBACrD,IAAI,UAAU,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;oBAC/B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC;gBACxE,CAAC;YACH,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,WAAW;QACjB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;IAC1B,CAAC;wGApCU,kBAAkB;4FAAlB,kBAAkB,gIACZ,sBAAsB,6BAJ7B,gBAAgB;;4FAGf,kBAAkB;kBAP9B,SAAS;+BACE,oBAAoB,cAClB,IAAI,WACP,CAAC,sBAAsB,CAAC,YACvB,gBAAgB;;sBAIzB,eAAe;uBAAC,sBAAsB;;sBAEtC,KAAK","sourcesContent":["import {\n AfterContentInit,\n Component,\n ContentChildren,\n Input,\n OnDestroy,\n QueryList,\n} from '@angular/core';\nimport { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { AccordionItemComponent } from './accordion-item';\n\n@Component({\n selector: 'compsych-accordion',\n standalone: true,\n imports: [AccordionItemComponent],\n template: `<ng-content />`,\n styleUrl: './accordion.css',\n})\nexport class AccordionComponent implements AfterContentInit, OnDestroy {\n @ContentChildren(AccordionItemComponent) items!: QueryList<AccordionItemComponent>;\n\n @Input()\n get multi(): boolean { return this._multi; }\n set multi(value: BooleanInput) { this._multi = coerceBooleanProperty(value); }\n private _multi = false;\n\n private subscriptions: { unsubscribe(): void }[] = [];\n\n ngAfterContentInit(): void {\n this.wireItems();\n this.items.changes.subscribe(() => {\n this.unwireItems();\n this.wireItems();\n });\n }\n\n ngOnDestroy(): void {\n this.unwireItems();\n }\n\n private wireItems(): void {\n this.items.forEach(item => {\n const sub = item.expandedChange.subscribe(isExpanded => {\n if (isExpanded && !this._multi) {\n this.items.filter(i => i !== item).forEach(i => (i.expanded = false));\n }\n });\n this.subscriptions.push(sub);\n });\n }\n\n private unwireItems(): void {\n this.subscriptions.forEach(s => s.unsubscribe());\n this.subscriptions = [];\n }\n}\n"]}
1
+ {"version":3,"file":"accordion.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/accordion/accordion.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n ElementRef,\n EventEmitter,\n forwardRef,\n HostListener,\n Input,\n Output,\n SecurityContext,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\nimport { ButtonComponent } from '../button/button';\nimport { AccordionButton, AccordionItem, AccordionVariant, NestedAccordionItem } from './accordion-types';\n\nexport type { AccordionButton, AccordionItem, AccordionVariant, NestedAccordionItem };\n\nlet nextId = 0;\n\n// AccordionItemComponent is defined before AccordionComponent because AccordionComponent\n// uses it directly in its imports. AccordionItemComponent references AccordionComponent\n// via forwardRef to break the forward-reference cycle within this file.\n@Component({\n selector: 'compsych-accordion-item',\n standalone: true,\n imports: [ButtonComponent, forwardRef(() => AccordionComponent)],\n template: `\n <button\n class=\"trigger\"\n type=\"button\"\n [id]=\"triggerId\"\n [attr.aria-expanded]=\"expanded\"\n [attr.aria-controls]=\"panelId\"\n (click)=\"toggle()\"\n >\n @if (iconSvg) {\n <span class=\"trigger-leading-icon\" [innerHTML]=\"iconSvg\" aria-hidden=\"true\"></span>\n }\n <span class=\"trigger-label\">{{ title }}</span>\n <span class=\"trigger-toggle material-symbols-rounded\" aria-hidden=\"true\">{{ expanded ? 'remove' : 'add' }}</span>\n </button>\n <div\n [id]=\"panelId\"\n role=\"region\"\n [attr.aria-labelledby]=\"triggerId\"\n [hidden]=\"!expanded\"\n >\n @if (content) {\n <div class=\"content\" [innerHTML]=\"sanitizedContent\"></div>\n }\n @if (nestedContent.length) {\n <compsych-accordion [items]=\"nestedContent\" variant=\"level-2\" [nested]=\"true\" />\n }\n @if (buttons.length) {\n <div class=\"actions\">\n @for (btn of buttons; track btn.label) {\n <compsych-button [label]=\"btn.label\" variant=\"filled\" size=\"small\" (click)=\"btn.action()\" />\n }\n </div>\n }\n </div>\n `,\n styleUrl: './accordion-item.css',\n host: {\n class: 'compsych-accordion-item',\n '[class.level-2]': 'variant === \"level-2\"',\n '[class.text-only]': 'variant === \"text-only\"',\n '[class.nested]': 'nested',\n },\n})\nexport class AccordionItemComponent {\n readonly triggerId = `accordion-trigger-${nextId}`;\n readonly panelId = `accordion-panel-${nextId++}`;\n\n @Input() title = '';\n @Input() content = '';\n @Input() buttons: AccordionButton[] = [];\n @Input() nestedContent: NestedAccordionItem[] = [];\n @Input() variant: AccordionVariant = 'default';\n @Input() nested = false;\n\n @Input()\n set icon(name: string) {\n const svg = LUCIDE_ICONS[name];\n this.iconSvg = svg ? this.sanitizer.bypassSecurityTrustHtml(svg) : null;\n }\n\n iconSvg: SafeHtml | null = null;\n\n get sanitizedContent(): SafeHtml {\n return this.sanitizer.sanitize(SecurityContext.HTML, this.content) ?? '';\n }\n\n constructor(\n private readonly sanitizer: DomSanitizer,\n private readonly el: ElementRef<HTMLElement>,\n ) {}\n\n @Input()\n get expanded(): boolean { return this._expanded; }\n set expanded(value: BooleanInput) { this._expanded = coerceBooleanProperty(value); }\n private _expanded = false;\n\n @Output() expandedChange = new EventEmitter<boolean>();\n\n toggle(): void {\n this._expanded = !this._expanded;\n this.expandedChange.emit(this._expanded);\n }\n\n @HostListener('keydown', ['$event'])\n onKeydown(event: KeyboardEvent): void {\n const target = event.target as HTMLElement;\n if (!(target instanceof HTMLButtonElement) || !target.classList.contains('trigger')) return;\n\n const parent = this.el.nativeElement.parentElement;\n if (!parent) return;\n\n const triggers = Array.from(\n parent.querySelectorAll<HTMLButtonElement>(':scope > compsych-accordion-item > button.trigger'),\n );\n const currentIndex = triggers.indexOf(target);\n let focusIndex = -1;\n\n switch (event.key) {\n case 'ArrowDown': focusIndex = currentIndex + 1; break;\n case 'ArrowUp': focusIndex = currentIndex - 1; break;\n case 'Home': focusIndex = 0; break;\n case 'End': focusIndex = triggers.length - 1; break;\n default: return;\n }\n\n if (focusIndex >= 0 && focusIndex < triggers.length) {\n triggers[focusIndex].focus();\n event.preventDefault();\n }\n }\n}\n\n@Component({\n selector: 'compsych-accordion',\n standalone: true,\n imports: [AccordionItemComponent],\n template: `\n @for (item of items; track $index; let i = $index) {\n <compsych-accordion-item\n [title]=\"item.title\"\n [content]=\"item.content ?? ''\"\n [icon]=\"item.icon ?? ''\"\n [buttons]=\"item.buttons ?? []\"\n [nestedContent]=\"item.nestedContent ?? []\"\n [variant]=\"variant\"\n [nested]=\"nested\"\n [expanded]=\"expandedSet.has(i)\"\n (expandedChange)=\"onExpandedChange(i, $event)\"\n />\n }\n `,\n styleUrl: './accordion.css',\n host: {\n '[class.level-2]': 'variant === \"level-2\"',\n '[class.text-only]': 'variant === \"text-only\"',\n '[class.nested]': 'nested',\n },\n})\nexport class AccordionComponent {\n @Input() items: AccordionItem[] = [];\n @Input() variant: AccordionVariant = 'default';\n @Input() nested = false;\n\n @Input()\n get multi(): boolean { return this._multi; }\n set multi(value: BooleanInput) { this._multi = coerceBooleanProperty(value); }\n private _multi = false;\n\n expandedSet = new Set<number>();\n\n onExpandedChange(index: number, isExpanded: boolean): void {\n if (isExpanded) {\n if (!this._multi) this.expandedSet.clear();\n this.expandedSet.add(index);\n } else {\n this.expandedSet.delete(index);\n }\n this.expandedSet = new Set(this.expandedSet);\n }\n}\n"]}
@@ -1,5 +1,4 @@
1
- import { Component, HostBinding, Input, ElementRef, inject, } from '@angular/core';
2
- import { NgIf } from '@angular/common';
1
+ import { Component, HostBinding, HostListener, Input, ElementRef, inject, } from '@angular/core';
3
2
  import { FocusMonitor } from '@angular/cdk/a11y';
4
3
  import { coerceBooleanProperty } from '@angular/cdk/coercion';
5
4
  import * as i0 from "@angular/core";
@@ -14,7 +13,10 @@ export class ButtonComponent {
14
13
  set iconOnly(value) { this._iconOnly = coerceBooleanProperty(value); }
15
14
  _iconOnly = false;
16
15
  get disabled() { return this._disabled; }
17
- set disabled(value) { this._disabled = coerceBooleanProperty(value); }
16
+ set disabled(value) {
17
+ this._disabled = coerceBooleanProperty(value);
18
+ this.elementRef.nativeElement.tabIndex = this._disabled ? -1 : 0;
19
+ }
18
20
  _disabled = false;
19
21
  _focused = false;
20
22
  _focusOrigin = null;
@@ -26,9 +28,27 @@ export class ButtonComponent {
26
28
  this._focused && this._focusOrigin === 'keyboard' ? 'focus-keyboard' : '',
27
29
  ].filter(Boolean).join(' ');
28
30
  }
31
+ get ariaDisabled() {
32
+ return this._disabled ? 'true' : null;
33
+ }
29
34
  get attrDisabled() {
30
35
  return this._disabled ? '' : null;
31
36
  }
37
+ onEnterKeydown(event) {
38
+ if (!this._disabled) {
39
+ event.preventDefault();
40
+ this.elementRef.nativeElement.click();
41
+ }
42
+ }
43
+ // Space: prevent scroll on keydown; fire action on keyup (matches native button behaviour).
44
+ onSpaceKeydown(event) {
45
+ event.preventDefault();
46
+ }
47
+ onSpaceKeyup() {
48
+ if (!this._disabled) {
49
+ this.elementRef.nativeElement.click();
50
+ }
51
+ }
32
52
  ngAfterViewInit() {
33
53
  this.focusMonitor.monitor(this.elementRef).subscribe(origin => {
34
54
  this._focused = !!origin;
@@ -39,17 +59,24 @@ export class ButtonComponent {
39
59
  this.focusMonitor.stopMonitoring(this.elementRef);
40
60
  }
41
61
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
42
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: ButtonComponent, isStandalone: true, selector: "compsych-button", inputs: { label: "label", variant: "variant", size: "size", icon: "icon", iconOnly: "iconOnly", disabled: "disabled" }, host: { properties: { "class": "this.hostClass", "attr.disabled": "this.attrDisabled" } }, ngImport: i0, template: `
43
- <span *ngIf="icon" class="icon material-symbols-rounded" aria-hidden="true">{{ icon }}</span>
62
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: ButtonComponent, isStandalone: true, selector: "compsych-button", inputs: { label: "label", variant: "variant", size: "size", icon: "icon", iconOnly: "iconOnly", disabled: "disabled" }, host: { attributes: { "tabindex": "0", "role": "button" }, listeners: { "keydown.enter": "onEnterKeydown($event)", "keydown.space": "onSpaceKeydown($event)", "keyup.space": "onSpaceKeyup()" }, properties: { "class": "this.hostClass", "attr.aria-disabled": "this.ariaDisabled", "attr.disabled": "this.attrDisabled" } }, ngImport: i0, template: `
63
+ @if (icon) {
64
+ <span class="icon material-symbols-rounded" aria-hidden="true">{{ icon }}</span>
65
+ }
44
66
  <span class="label">{{ label }}</span>
45
- `, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;border-radius:var(--sys-radius-full);border:none;cursor:pointer;white-space:nowrap;position:relative;overflow:hidden}:host:before{content:\"\";position:absolute;inset:0;border-radius:inherit;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}:host(:hover):before{opacity:.08}:host(:active):before{opacity:.1}:host(.focus-keyboard):before{opacity:.12}:host(.focus-keyboard){outline:var(--sys-stroke-thick) solid var(--sys-on-primary-fixed-variant);outline-offset:var(--sys-stroke-thick)}:host:disabled,:host([disabled]){cursor:not-allowed;opacity:.38;pointer-events:none}.icon{font-size:inherit;line-height:1;-webkit-user-select:none;user-select:none}:host(.icon-only){padding:0;aspect-ratio:1}:host(.icon-only.small){width:2rem}:host(.icon-only.medium){width:2.5rem}:host(.icon-only.large){width:3rem}:host(.icon-only.xlarge){width:3.5rem}:host(.small){gap:var(--sys-padding-4);height:2rem;padding:var(--sys-padding-4) var(--sys-padding-12);font-family:var(--sys-font-family);font-size:var(--sys-font-size);font-weight:var(--sys-font-weight-emphasized);line-height:var(--sys-line-height);letter-spacing:var(--sys-tracking)}:host(.medium){gap:var(--sys-padding-8);height:2.5rem;padding:var(--sys-padding-8) var(--sys-padding-16);font-family:var(--sys-font-family);font-size:var(--sys-font-size);font-weight:var(--sys-font-weight-emphasized);line-height:var(--sys-line-height);letter-spacing:var(--sys-tracking)}:host(.large){gap:var(--sys-padding-8);height:3rem;padding:var(--sys-padding-8) var(--sys-padding-24);font-family:var(--sys-font-family);font-size:var(--sys-font-size);font-weight:var(--sys-font-weight-emphasized);line-height:var(--sys-line-height);letter-spacing:var(--sys-tracking)}:host(.xlarge){gap:var(--sys-padding-12);height:3.5rem;padding:var(--sys-padding-8) var(--sys-padding-24);font-family:var(--sys-font-family);font-size:var(--sys-font-size);font-weight:var(--sys-font-weight-emphasized);line-height:var(--sys-line-height);letter-spacing:var(--sys-tracking)}:host(.filled){background-color:var(--sys-primary);color:var(--sys-on-primary)}:host(.tonal){background-color:var(--sys-primary-fixed-dim);color:var(--sys-on-primary-fixed)}:host(.outlined){background-color:transparent;color:var(--sys-on-surface);border:var(--sys-stroke-thin) solid var(--sys-outline)}:host(.elevated){background-color:var(--sys-surface-container-lowest);color:var(--sys-on-surface);box-shadow:0 .125rem .25rem #00000014,0 .25rem 1rem var(--sys-black-10)}:host(.text){background-color:transparent;color:var(--sys-on-surface);padding-inline:0}:host(.danger){background-color:var(--sys-error);color:var(--sys-on-error)}:host(.danger-outlined){background-color:transparent;color:var(--sys-error);border:var(--sys-stroke-thin) solid var(--sys-error-container)}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
67
+ `, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;border-radius:var(--border-radius-sys-radius-full);border:none;cursor:pointer;white-space:nowrap;position:relative;overflow:hidden}:host:before{content:\"\";position:absolute;inset:0;border-radius:inherit;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}:host(:hover):before{opacity:.08}:host(:active):before{opacity:.1}:host(.focus-keyboard):before{opacity:.12}:host(.focus-keyboard){outline:var(--border-width-sys-stroke-thick) solid var(--add-on-primary-fixed-sys-on-primary-fixed-variant);outline-offset:var(--border-width-sys-stroke-thick)}:host:disabled,:host([disabled]){cursor:not-allowed;opacity:.38;pointer-events:none}.icon{font-size:inherit;line-height:1;-webkit-user-select:none;user-select:none}:host(.icon-only){padding:0;aspect-ratio:1}:host(.icon-only.small){width:2rem}:host(.icon-only.medium){width:2.5rem}:host(.icon-only.large){width:3rem}:host(.icon-only.xlarge){width:3.5rem}:host(.small){gap:var(--spacing-padding-sys-padding-4);height:2rem;padding:var(--spacing-padding-sys-padding-4) var(--spacing-padding-sys-padding-12);font-family:var(--label-small-sys-font-family);font-size:var(--label-small-sys-font-size);font-weight:var(--label-small-sys-font-weight-emphasized);line-height:var(--label-small-sys-line-height);letter-spacing:var(--label-small-sys-tracking)}:host(.medium){gap:var(--spacing-padding-sys-padding-8);height:2.5rem;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-16);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);letter-spacing:var(--label-medium-sys-tracking)}:host(.large){gap:var(--spacing-padding-sys-padding-8);height:3rem;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}:host(.xlarge){gap:var(--spacing-padding-sys-padding-12);height:3.5rem;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}:host(.filled){background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary)}:host(.tonal){background-color:var(--add-on-primary-fixed-sys-primary-fixed-dim);color:var(--add-on-primary-fixed-sys-on-primary-fixed)}:host(.outlined){background-color:transparent;color:var(--surface-surface-sys-on-surface);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host(.elevated){background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);box-shadow:0 .125rem .25rem #00000014,0 .25rem 1rem var(--transparent-neutral-sys-black-10)}:host(.text){background-color:transparent;color:var(--surface-surface-sys-on-surface);padding-inline:0}:host(.danger){background-color:var(--error-sys-error);color:var(--error-sys-on-error)}:host(.danger-outlined){background-color:transparent;color:var(--error-sys-error);border:var(--border-width-sys-stroke-thin) solid var(--error-sys-error-container)}\n"] });
46
68
  }
47
69
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ButtonComponent, decorators: [{
48
70
  type: Component,
49
- args: [{ selector: 'compsych-button', standalone: true, imports: [NgIf], template: `
50
- <span *ngIf="icon" class="icon material-symbols-rounded" aria-hidden="true">{{ icon }}</span>
71
+ args: [{ selector: 'compsych-button', standalone: true, template: `
72
+ @if (icon) {
73
+ <span class="icon material-symbols-rounded" aria-hidden="true">{{ icon }}</span>
74
+ }
51
75
  <span class="label">{{ label }}</span>
52
- `, styles: [":host{display:inline-flex;align-items:center;justify-content:center;border-radius:var(--sys-radius-full);border:none;cursor:pointer;white-space:nowrap;position:relative;overflow:hidden}:host:before{content:\"\";position:absolute;inset:0;border-radius:inherit;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}:host(:hover):before{opacity:.08}:host(:active):before{opacity:.1}:host(.focus-keyboard):before{opacity:.12}:host(.focus-keyboard){outline:var(--sys-stroke-thick) solid var(--sys-on-primary-fixed-variant);outline-offset:var(--sys-stroke-thick)}:host:disabled,:host([disabled]){cursor:not-allowed;opacity:.38;pointer-events:none}.icon{font-size:inherit;line-height:1;-webkit-user-select:none;user-select:none}:host(.icon-only){padding:0;aspect-ratio:1}:host(.icon-only.small){width:2rem}:host(.icon-only.medium){width:2.5rem}:host(.icon-only.large){width:3rem}:host(.icon-only.xlarge){width:3.5rem}:host(.small){gap:var(--sys-padding-4);height:2rem;padding:var(--sys-padding-4) var(--sys-padding-12);font-family:var(--sys-font-family);font-size:var(--sys-font-size);font-weight:var(--sys-font-weight-emphasized);line-height:var(--sys-line-height);letter-spacing:var(--sys-tracking)}:host(.medium){gap:var(--sys-padding-8);height:2.5rem;padding:var(--sys-padding-8) var(--sys-padding-16);font-family:var(--sys-font-family);font-size:var(--sys-font-size);font-weight:var(--sys-font-weight-emphasized);line-height:var(--sys-line-height);letter-spacing:var(--sys-tracking)}:host(.large){gap:var(--sys-padding-8);height:3rem;padding:var(--sys-padding-8) var(--sys-padding-24);font-family:var(--sys-font-family);font-size:var(--sys-font-size);font-weight:var(--sys-font-weight-emphasized);line-height:var(--sys-line-height);letter-spacing:var(--sys-tracking)}:host(.xlarge){gap:var(--sys-padding-12);height:3.5rem;padding:var(--sys-padding-8) var(--sys-padding-24);font-family:var(--sys-font-family);font-size:var(--sys-font-size);font-weight:var(--sys-font-weight-emphasized);line-height:var(--sys-line-height);letter-spacing:var(--sys-tracking)}:host(.filled){background-color:var(--sys-primary);color:var(--sys-on-primary)}:host(.tonal){background-color:var(--sys-primary-fixed-dim);color:var(--sys-on-primary-fixed)}:host(.outlined){background-color:transparent;color:var(--sys-on-surface);border:var(--sys-stroke-thin) solid var(--sys-outline)}:host(.elevated){background-color:var(--sys-surface-container-lowest);color:var(--sys-on-surface);box-shadow:0 .125rem .25rem #00000014,0 .25rem 1rem var(--sys-black-10)}:host(.text){background-color:transparent;color:var(--sys-on-surface);padding-inline:0}:host(.danger){background-color:var(--sys-error);color:var(--sys-on-error)}:host(.danger-outlined){background-color:transparent;color:var(--sys-error);border:var(--sys-stroke-thin) solid var(--sys-error-container)}\n"] }]
76
+ `, host: {
77
+ 'tabindex': '0',
78
+ 'role': 'button',
79
+ }, styles: [":host{display:inline-flex;align-items:center;justify-content:center;border-radius:var(--border-radius-sys-radius-full);border:none;cursor:pointer;white-space:nowrap;position:relative;overflow:hidden}:host:before{content:\"\";position:absolute;inset:0;border-radius:inherit;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}:host(:hover):before{opacity:.08}:host(:active):before{opacity:.1}:host(.focus-keyboard):before{opacity:.12}:host(.focus-keyboard){outline:var(--border-width-sys-stroke-thick) solid var(--add-on-primary-fixed-sys-on-primary-fixed-variant);outline-offset:var(--border-width-sys-stroke-thick)}:host:disabled,:host([disabled]){cursor:not-allowed;opacity:.38;pointer-events:none}.icon{font-size:inherit;line-height:1;-webkit-user-select:none;user-select:none}:host(.icon-only){padding:0;aspect-ratio:1}:host(.icon-only.small){width:2rem}:host(.icon-only.medium){width:2.5rem}:host(.icon-only.large){width:3rem}:host(.icon-only.xlarge){width:3.5rem}:host(.small){gap:var(--spacing-padding-sys-padding-4);height:2rem;padding:var(--spacing-padding-sys-padding-4) var(--spacing-padding-sys-padding-12);font-family:var(--label-small-sys-font-family);font-size:var(--label-small-sys-font-size);font-weight:var(--label-small-sys-font-weight-emphasized);line-height:var(--label-small-sys-line-height);letter-spacing:var(--label-small-sys-tracking)}:host(.medium){gap:var(--spacing-padding-sys-padding-8);height:2.5rem;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-16);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);letter-spacing:var(--label-medium-sys-tracking)}:host(.large){gap:var(--spacing-padding-sys-padding-8);height:3rem;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}:host(.xlarge){gap:var(--spacing-padding-sys-padding-12);height:3.5rem;padding:var(--spacing-padding-sys-padding-8) var(--spacing-padding-sys-padding-24);font-family:var(--label-large-sys-font-family);font-size:var(--label-large-sys-font-size);font-weight:var(--label-large-sys-font-weight-emphasized);line-height:var(--label-large-sys-line-height);letter-spacing:var(--label-large-sys-tracking)}:host(.filled){background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary)}:host(.tonal){background-color:var(--add-on-primary-fixed-sys-primary-fixed-dim);color:var(--add-on-primary-fixed-sys-on-primary-fixed)}:host(.outlined){background-color:transparent;color:var(--surface-surface-sys-on-surface);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}:host(.elevated){background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);box-shadow:0 .125rem .25rem #00000014,0 .25rem 1rem var(--transparent-neutral-sys-black-10)}:host(.text){background-color:transparent;color:var(--surface-surface-sys-on-surface);padding-inline:0}:host(.danger){background-color:var(--error-sys-error);color:var(--error-sys-on-error)}:host(.danger-outlined){background-color:transparent;color:var(--error-sys-error);border:var(--border-width-sys-stroke-thin) solid var(--error-sys-error-container)}\n"] }]
53
80
  }], propDecorators: { label: [{
54
81
  type: Input
55
82
  }], variant: [{
@@ -65,8 +92,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
65
92
  }], hostClass: [{
66
93
  type: HostBinding,
67
94
  args: ['class']
95
+ }], ariaDisabled: [{
96
+ type: HostBinding,
97
+ args: ['attr.aria-disabled']
68
98
  }], attrDisabled: [{
69
99
  type: HostBinding,
70
100
  args: ['attr.disabled']
101
+ }], onEnterKeydown: [{
102
+ type: HostListener,
103
+ args: ['keydown.enter', ['$event']]
104
+ }], onSpaceKeydown: [{
105
+ type: HostListener,
106
+ args: ['keydown.space', ['$event']]
107
+ }], onSpaceKeyup: [{
108
+ type: HostListener,
109
+ args: ['keyup.space']
71
110
  }] } });
72
111
  //# sourceMappingURL=button.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"button.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/button/button.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,WAAW,EACX,KAAK,EACL,UAAU,EAGV,MAAM,GACP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,YAAY,EAAe,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAgB,MAAM,uBAAuB,CAAC;;AAY5E,MAAM,OAAO,eAAe;IACT,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IACpC,UAAU,GAAG,MAAM,CAAC,CAAA,UAAuB,CAAA,CAAC,CAAC;IAErD,KAAK,GAAG,EAAE,CAAC;IACX,OAAO,GAAyF,QAAQ,CAAC;IACzG,IAAI,GAA4C,OAAO,CAAC;IACxD,IAAI,CAAU;IAEvB,IACI,QAAQ,KAAc,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAClD,IAAI,QAAQ,CAAC,KAAmB,IAAI,IAAI,CAAC,SAAS,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5E,SAAS,GAAG,KAAK,CAAC;IAE1B,IACI,QAAQ,KAAc,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAClD,IAAI,QAAQ,CAAC,KAAmB,IAAI,IAAI,CAAC,SAAS,GAAG,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5E,SAAS,GAAG,KAAK,CAAC;IAEhB,QAAQ,GAAG,KAAK,CAAC;IACjB,YAAY,GAAgB,IAAI,CAAC;IAE3C,IAA0B,SAAS;QACjC,OAAO;YACL,IAAI,CAAC,OAAO;YACZ,IAAI,CAAC,IAAI;YACT,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE;YACjC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,YAAY,KAAK,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE;SAC1E,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,IAAkC,YAAY;QAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACpC,CAAC;IAED,eAAe;QACb,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE;YAC5D,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,MAAM,CAAC;YACzB,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;QAC7B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpD,CAAC;wGA5CU,eAAe;4FAAf,eAAe,8RANhB;;;GAGT,y3FAJS,IAAI;;4FAOH,eAAe;kBAV3B,SAAS;+BACE,iBAAiB,cACf,IAAI,WACP,CAAC,IAAI,CAAC,YACL;;;GAGT;;sBAOA,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBAEL,KAAK;;sBAKL,KAAK;;sBAQL,WAAW;uBAAC,OAAO;;sBASnB,WAAW;uBAAC,eAAe","sourcesContent":["import {\r\n Component,\r\n HostBinding,\r\n Input,\r\n ElementRef,\r\n AfterViewInit,\r\n OnDestroy,\r\n inject,\r\n} from '@angular/core';\r\nimport { NgIf } from '@angular/common';\r\nimport { FocusMonitor, FocusOrigin } from '@angular/cdk/a11y';\r\nimport { coerceBooleanProperty, BooleanInput } from '@angular/cdk/coercion';\r\n\r\n@Component({\r\n selector: 'compsych-button',\r\n standalone: true,\r\n imports: [NgIf],\r\n template: `\r\n <span *ngIf=\"icon\" class=\"icon material-symbols-rounded\" aria-hidden=\"true\">{{ icon }}</span>\r\n <span class=\"label\">{{ label }}</span>\r\n `,\r\n styleUrl: './button.css',\r\n})\r\nexport class ButtonComponent implements AfterViewInit, OnDestroy {\r\n private readonly focusMonitor = inject(FocusMonitor);\r\n private readonly elementRef = inject(ElementRef<HTMLElement>);\r\n\r\n @Input() label = '';\r\n @Input() variant: 'filled' | 'tonal' | 'outlined' | 'elevated' | 'text' | 'danger' | 'danger-outlined' = 'filled';\r\n @Input() size: 'small' | 'medium' | 'large' | 'xlarge' = 'small';\r\n @Input() icon?: string;\r\n\r\n @Input()\r\n get iconOnly(): boolean { return this._iconOnly; }\r\n set iconOnly(value: BooleanInput) { this._iconOnly = coerceBooleanProperty(value); }\r\n private _iconOnly = false;\r\n\r\n @Input()\r\n get disabled(): boolean { return this._disabled; }\r\n set disabled(value: BooleanInput) { this._disabled = coerceBooleanProperty(value); }\r\n private _disabled = false;\r\n\r\n protected _focused = false;\r\n protected _focusOrigin: FocusOrigin = null;\r\n\r\n @HostBinding('class') get hostClass(): string {\r\n return [\r\n this.variant,\r\n this.size,\r\n this._iconOnly ? 'icon-only' : '',\r\n this._focused && this._focusOrigin === 'keyboard' ? 'focus-keyboard' : '',\r\n ].filter(Boolean).join(' ');\r\n }\r\n\r\n @HostBinding('attr.disabled') get attrDisabled(): '' | null {\r\n return this._disabled ? '' : null;\r\n }\r\n\r\n ngAfterViewInit(): void {\r\n this.focusMonitor.monitor(this.elementRef).subscribe(origin => {\r\n this._focused = !!origin;\r\n this._focusOrigin = origin;\r\n });\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.focusMonitor.stopMonitoring(this.elementRef);\r\n }\r\n}\r\n"]}
1
+ {"version":3,"file":"button.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/button/button.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n Component,\r\n HostBinding,\r\n HostListener,\r\n Input,\r\n ElementRef,\r\n AfterViewInit,\r\n OnDestroy,\r\n inject,\r\n} from '@angular/core';\r\nimport { FocusMonitor, FocusOrigin } from '@angular/cdk/a11y';\r\n\r\nexport type ButtonVariant = 'filled' | 'tonal' | 'outlined' | 'elevated' | 'text' | 'danger' | 'danger-outlined';\r\nimport { coerceBooleanProperty, BooleanInput } from '@angular/cdk/coercion';\r\n\r\n@Component({\r\n selector: 'compsych-button',\r\n standalone: true,\r\n template: `\r\n @if (icon) {\r\n <span class=\"icon material-symbols-rounded\" aria-hidden=\"true\">{{ icon }}</span>\r\n }\r\n <span class=\"label\">{{ label }}</span>\r\n `,\r\n styleUrl: './button.css',\r\n host: {\r\n 'tabindex': '0',\r\n 'role': 'button',\r\n },\r\n})\r\nexport class ButtonComponent implements AfterViewInit, OnDestroy {\r\n private readonly focusMonitor = inject(FocusMonitor);\r\n private readonly elementRef = inject(ElementRef<HTMLElement>);\r\n\r\n @Input() label = '';\r\n @Input() variant: ButtonVariant = 'filled';\r\n @Input() size: 'small' | 'medium' | 'large' | 'xlarge' = 'small';\r\n @Input() icon?: string;\r\n\r\n @Input()\r\n get iconOnly(): boolean { return this._iconOnly; }\r\n set iconOnly(value: BooleanInput) { this._iconOnly = coerceBooleanProperty(value); }\r\n private _iconOnly = false;\r\n\r\n @Input()\r\n get disabled(): boolean { return this._disabled; }\r\n set disabled(value: BooleanInput) {\r\n this._disabled = coerceBooleanProperty(value);\r\n this.elementRef.nativeElement.tabIndex = this._disabled ? -1 : 0;\r\n }\r\n private _disabled = false;\r\n\r\n protected _focused = false;\r\n protected _focusOrigin: FocusOrigin = null;\r\n\r\n @HostBinding('class') get hostClass(): string {\r\n return [\r\n this.variant,\r\n this.size,\r\n this._iconOnly ? 'icon-only' : '',\r\n this._focused && this._focusOrigin === 'keyboard' ? 'focus-keyboard' : '',\r\n ].filter(Boolean).join(' ');\r\n }\r\n\r\n @HostBinding('attr.aria-disabled') get ariaDisabled(): string | null {\r\n return this._disabled ? 'true' : null;\r\n }\r\n\r\n @HostBinding('attr.disabled') get attrDisabled(): '' | null {\r\n return this._disabled ? '' : null;\r\n }\r\n\r\n @HostListener('keydown.enter', ['$event'])\r\n onEnterKeydown(event: Event): void {\r\n if (!this._disabled) {\r\n event.preventDefault();\r\n this.elementRef.nativeElement.click();\r\n }\r\n }\r\n\r\n // Space: prevent scroll on keydown; fire action on keyup (matches native button behaviour).\r\n @HostListener('keydown.space', ['$event'])\r\n onSpaceKeydown(event: Event): void {\r\n event.preventDefault();\r\n }\r\n\r\n @HostListener('keyup.space')\r\n onSpaceKeyup(): void {\r\n if (!this._disabled) {\r\n this.elementRef.nativeElement.click();\r\n }\r\n }\r\n\r\n ngAfterViewInit(): void {\r\n this.focusMonitor.monitor(this.elementRef).subscribe(origin => {\r\n this._focused = !!origin;\r\n this._focusOrigin = origin;\r\n });\r\n }\r\n\r\n ngOnDestroy(): void {\r\n this.focusMonitor.stopMonitoring(this.elementRef);\r\n }\r\n}\r\n"]}