@compsych-ui-components/angular 6.0.10 → 6.1.1
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.map +1 -1
- package/esm2022/lib/accordion/accordion-types.js.map +1 -1
- package/esm2022/lib/accordion/accordion.js +99 -99
- package/esm2022/lib/accordion/accordion.js.map +1 -1
- package/esm2022/lib/button/button.js +101 -85
- package/esm2022/lib/button/button.js.map +1 -1
- package/esm2022/lib/header-nav/header-nav.js +99 -17
- package/esm2022/lib/header-nav/header-nav.js.map +1 -1
- package/esm2022/lib/sidebar/sidebar.js +2 -2
- package/esm2022/lib/sidebar/sidebar.js.map +1 -1
- package/index.d.ts +1 -1
- package/lib/button/button.d.ts +43 -29
- package/lib/header-nav/header-nav.d.ts +74 -25
- package/package.json +1 -1
package/esm2022/index.js.map
CHANGED
|
@@ -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,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"]}
|
|
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';\r\nimport { ButtonComponent } from './lib/button/button';\r\nimport { AccordionComponent } from './lib/accordion/accordion';\r\nimport { HeaderNavComponent } from './lib/header-nav/header-nav';\r\nimport { SidebarComponent } from './lib/sidebar/sidebar';\r\n\r\nexport { ButtonComponent } from './lib/button/button';\r\nexport type { ButtonVariant, ButtonSize, ButtonIconPosition, 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 { 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\n\r\n@NgModule({\r\n imports: [ButtonComponent, AccordionComponent, HeaderNavComponent, SidebarComponent],\r\n exports: [ButtonComponent, AccordionComponent, HeaderNavComponent, SidebarComponent],\r\n})\r\nexport class CompsychAngularModule {}\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accordion-types.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/accordion/accordion-types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Visual style variant applied to the accordion and all its items.\n * - `'default'` — standard card style with borders and a background surface.\n * - `'level-2'` — compact nested style used inside another accordion panel.\n * - `'text-only'` — no card chrome; plain text trigger rows.\n */\nexport type AccordionVariant = 'default' | 'level-2' | 'text-only';\n\n/** An action button rendered at the bottom of an accordion panel. */\nexport interface AccordionButton {\n /** Text displayed on the button. */\n label: string;\n /** Called when the button is clicked. */\n action: () => void;\n}\n\n/**\n * A simplified accordion item used inside `AccordionItem.nestedContent`.\n * Identical to `AccordionItem` but without `buttons` and `nestedContent` —\n * nesting beyond one level is not supported.\n */\nexport type NestedAccordionItem = Omit<AccordionItem, 'buttons' | 'nestedContent'>;\n\n/** Data model for a single expandable row in a `compsych-accordion`. */\nexport interface AccordionItem {\n /** Text shown in the trigger button header. */\n title: string;\n /** HTML string rendered in the panel body when expanded. Sanitized before display. Omit when using `nestedContent`. */\n content?: string;\n /** Lucide icon name (kebab-case) shown to the left of the title, e.g. `'globe'` or `'folder'`. */\n icon?: string;\n /** Whether this panel starts expanded on initial render. Defaults to false. */\n expanded?: boolean;\n /** Action buttons rendered at the bottom of the panel, below `content`. */\n buttons?: AccordionButton[];\n /** Items rendered as a nested level-2 accordion inside this panel. Cannot contain `buttons` or further nesting. */\n nestedContent?: NestedAccordionItem[];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"accordion-types.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/accordion/accordion-types.ts"],"names":[],"mappings":"","sourcesContent":["/**\r\n * Visual style variant applied to the accordion and all its items.\r\n * - `'default'` — standard card style with borders and a background surface.\r\n * - `'level-2'` — compact nested style used inside another accordion panel.\r\n * - `'text-only'` — no card chrome; plain text trigger rows.\r\n */\r\nexport type AccordionVariant = 'default' | 'level-2' | 'text-only';\r\n\r\n/** An action button rendered at the bottom of an accordion panel. */\r\nexport interface AccordionButton {\r\n /** Text displayed on the button. */\r\n label: string;\r\n /** Called when the button is clicked. */\r\n action: () => void;\r\n}\r\n\r\n/**\r\n * A simplified accordion item used inside `AccordionItem.nestedContent`.\r\n * Identical to `AccordionItem` but without `buttons` and `nestedContent` —\r\n * nesting beyond one level is not supported.\r\n */\r\nexport type NestedAccordionItem = Omit<AccordionItem, 'buttons' | 'nestedContent'>;\r\n\r\n/** Data model for a single expandable row in a `compsych-accordion`. */\r\nexport interface AccordionItem {\r\n /** Text shown in the trigger button header. */\r\n title: string;\r\n /** HTML string rendered in the panel body when expanded. Sanitized before display. Omit when using `nestedContent`. */\r\n content?: string;\r\n /** Lucide icon name (kebab-case) shown to the left of the title, e.g. `'globe'` or `'folder'`. */\r\n icon?: string;\r\n /** Whether this panel starts expanded on initial render. Defaults to false. */\r\n expanded?: boolean;\r\n /** Action buttons rendered at the bottom of the panel, below `content`. */\r\n buttons?: AccordionButton[];\r\n /** Items rendered as a nested level-2 accordion inside this panel. Cannot contain `buttons` or further nesting. */\r\n nestedContent?: NestedAccordionItem[];\r\n}\r\n"]}
|
|
@@ -84,80 +84,80 @@ export class AccordionItemComponent {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
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 });
|
|
87
|
-
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: `
|
|
88
|
-
<button
|
|
89
|
-
class="trigger"
|
|
90
|
-
type="button"
|
|
91
|
-
[id]="triggerId"
|
|
92
|
-
[attr.aria-expanded]="expanded"
|
|
93
|
-
[attr.aria-controls]="panelId"
|
|
94
|
-
(click)="toggle()"
|
|
95
|
-
>
|
|
96
|
-
@if (iconSvg) {
|
|
97
|
-
<span class="trigger-leading-icon" [innerHTML]="iconSvg" aria-hidden="true"></span>
|
|
98
|
-
}
|
|
99
|
-
<span class="trigger-label">{{ title }}</span>
|
|
100
|
-
<span class="trigger-toggle material-symbols-rounded" aria-hidden="true">{{ expanded ? 'remove' : 'add' }}</span>
|
|
101
|
-
</button>
|
|
102
|
-
<div
|
|
103
|
-
[id]="panelId"
|
|
104
|
-
role="region"
|
|
105
|
-
[attr.aria-labelledby]="triggerId"
|
|
106
|
-
[hidden]="!expanded"
|
|
107
|
-
>
|
|
108
|
-
@if (content) {
|
|
109
|
-
<div class="content" [innerHTML]="sanitizedContent"></div>
|
|
110
|
-
}
|
|
111
|
-
@if (nestedContent.length) {
|
|
112
|
-
<compsych-accordion [items]="nestedContent" variant="level-2" [nested]="true" />
|
|
113
|
-
}
|
|
114
|
-
@if (buttons.length) {
|
|
115
|
-
<div class="actions">
|
|
116
|
-
@for (btn of buttons; track btn.label) {
|
|
117
|
-
<compsych-button [label]="btn.label" variant="filled" size="small" (click)="btn.action()" />
|
|
118
|
-
}
|
|
119
|
-
</div>
|
|
120
|
-
}
|
|
121
|
-
</div>
|
|
122
|
-
`, 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"] }] });
|
|
87
|
+
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: `
|
|
88
|
+
<button
|
|
89
|
+
class="trigger"
|
|
90
|
+
type="button"
|
|
91
|
+
[id]="triggerId"
|
|
92
|
+
[attr.aria-expanded]="expanded"
|
|
93
|
+
[attr.aria-controls]="panelId"
|
|
94
|
+
(click)="toggle()"
|
|
95
|
+
>
|
|
96
|
+
@if (iconSvg) {
|
|
97
|
+
<span class="trigger-leading-icon" [innerHTML]="iconSvg" aria-hidden="true"></span>
|
|
98
|
+
}
|
|
99
|
+
<span class="trigger-label">{{ title }}</span>
|
|
100
|
+
<span class="trigger-toggle material-symbols-rounded" aria-hidden="true">{{ expanded ? 'remove' : 'add' }}</span>
|
|
101
|
+
</button>
|
|
102
|
+
<div
|
|
103
|
+
[id]="panelId"
|
|
104
|
+
role="region"
|
|
105
|
+
[attr.aria-labelledby]="triggerId"
|
|
106
|
+
[hidden]="!expanded"
|
|
107
|
+
>
|
|
108
|
+
@if (content) {
|
|
109
|
+
<div class="content" [innerHTML]="sanitizedContent"></div>
|
|
110
|
+
}
|
|
111
|
+
@if (nestedContent.length) {
|
|
112
|
+
<compsych-accordion [items]="nestedContent" variant="level-2" [nested]="true" />
|
|
113
|
+
}
|
|
114
|
+
@if (buttons.length) {
|
|
115
|
+
<div class="actions">
|
|
116
|
+
@for (btn of buttons; track btn.label) {
|
|
117
|
+
<compsych-button [label]="btn.label" variant="filled" size="small" (click)="btn.action()" />
|
|
118
|
+
}
|
|
119
|
+
</div>
|
|
120
|
+
}
|
|
121
|
+
</div>
|
|
122
|
+
`, 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", "iconPosition", "type", "ariaLabel", "iconOnly", "disabled", "loading"] }, { kind: "component", type: i0.forwardRef(() => AccordionComponent), selector: "compsych-accordion", inputs: ["items", "variant", "nested", "multi"] }] });
|
|
123
123
|
}
|
|
124
124
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: AccordionItemComponent, decorators: [{
|
|
125
125
|
type: Component,
|
|
126
|
-
args: [{ selector: 'compsych-accordion-item', standalone: true, imports: [ButtonComponent, forwardRef(() => AccordionComponent)], template: `
|
|
127
|
-
<button
|
|
128
|
-
class="trigger"
|
|
129
|
-
type="button"
|
|
130
|
-
[id]="triggerId"
|
|
131
|
-
[attr.aria-expanded]="expanded"
|
|
132
|
-
[attr.aria-controls]="panelId"
|
|
133
|
-
(click)="toggle()"
|
|
134
|
-
>
|
|
135
|
-
@if (iconSvg) {
|
|
136
|
-
<span class="trigger-leading-icon" [innerHTML]="iconSvg" aria-hidden="true"></span>
|
|
137
|
-
}
|
|
138
|
-
<span class="trigger-label">{{ title }}</span>
|
|
139
|
-
<span class="trigger-toggle material-symbols-rounded" aria-hidden="true">{{ expanded ? 'remove' : 'add' }}</span>
|
|
140
|
-
</button>
|
|
141
|
-
<div
|
|
142
|
-
[id]="panelId"
|
|
143
|
-
role="region"
|
|
144
|
-
[attr.aria-labelledby]="triggerId"
|
|
145
|
-
[hidden]="!expanded"
|
|
146
|
-
>
|
|
147
|
-
@if (content) {
|
|
148
|
-
<div class="content" [innerHTML]="sanitizedContent"></div>
|
|
149
|
-
}
|
|
150
|
-
@if (nestedContent.length) {
|
|
151
|
-
<compsych-accordion [items]="nestedContent" variant="level-2" [nested]="true" />
|
|
152
|
-
}
|
|
153
|
-
@if (buttons.length) {
|
|
154
|
-
<div class="actions">
|
|
155
|
-
@for (btn of buttons; track btn.label) {
|
|
156
|
-
<compsych-button [label]="btn.label" variant="filled" size="small" (click)="btn.action()" />
|
|
157
|
-
}
|
|
158
|
-
</div>
|
|
159
|
-
}
|
|
160
|
-
</div>
|
|
126
|
+
args: [{ selector: 'compsych-accordion-item', standalone: true, imports: [ButtonComponent, forwardRef(() => AccordionComponent)], template: `
|
|
127
|
+
<button
|
|
128
|
+
class="trigger"
|
|
129
|
+
type="button"
|
|
130
|
+
[id]="triggerId"
|
|
131
|
+
[attr.aria-expanded]="expanded"
|
|
132
|
+
[attr.aria-controls]="panelId"
|
|
133
|
+
(click)="toggle()"
|
|
134
|
+
>
|
|
135
|
+
@if (iconSvg) {
|
|
136
|
+
<span class="trigger-leading-icon" [innerHTML]="iconSvg" aria-hidden="true"></span>
|
|
137
|
+
}
|
|
138
|
+
<span class="trigger-label">{{ title }}</span>
|
|
139
|
+
<span class="trigger-toggle material-symbols-rounded" aria-hidden="true">{{ expanded ? 'remove' : 'add' }}</span>
|
|
140
|
+
</button>
|
|
141
|
+
<div
|
|
142
|
+
[id]="panelId"
|
|
143
|
+
role="region"
|
|
144
|
+
[attr.aria-labelledby]="triggerId"
|
|
145
|
+
[hidden]="!expanded"
|
|
146
|
+
>
|
|
147
|
+
@if (content) {
|
|
148
|
+
<div class="content" [innerHTML]="sanitizedContent"></div>
|
|
149
|
+
}
|
|
150
|
+
@if (nestedContent.length) {
|
|
151
|
+
<compsych-accordion [items]="nestedContent" variant="level-2" [nested]="true" />
|
|
152
|
+
}
|
|
153
|
+
@if (buttons.length) {
|
|
154
|
+
<div class="actions">
|
|
155
|
+
@for (btn of buttons; track btn.label) {
|
|
156
|
+
<compsych-button [label]="btn.label" variant="filled" size="small" (click)="btn.action()" />
|
|
157
|
+
}
|
|
158
|
+
</div>
|
|
159
|
+
}
|
|
160
|
+
</div>
|
|
161
161
|
`, host: {
|
|
162
162
|
class: 'compsych-accordion-item',
|
|
163
163
|
'[class.level-2]': 'variant === "level-2"',
|
|
@@ -210,38 +210,38 @@ export class AccordionComponent {
|
|
|
210
210
|
this.expandedSet = new Set(this.expandedSet);
|
|
211
211
|
}
|
|
212
212
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: AccordionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
213
|
-
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: `
|
|
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
|
-
}
|
|
213
|
+
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: `
|
|
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
227
|
`, 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"] }] });
|
|
228
228
|
}
|
|
229
229
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: AccordionComponent, decorators: [{
|
|
230
230
|
type: Component,
|
|
231
|
-
args: [{ selector: 'compsych-accordion', standalone: true, imports: [AccordionItemComponent], template: `
|
|
232
|
-
@for (item of items; track $index; let i = $index) {
|
|
233
|
-
<compsych-accordion-item
|
|
234
|
-
[title]="item.title"
|
|
235
|
-
[content]="item.content ?? ''"
|
|
236
|
-
[icon]="item.icon ?? ''"
|
|
237
|
-
[buttons]="item.buttons ?? []"
|
|
238
|
-
[nestedContent]="item.nestedContent ?? []"
|
|
239
|
-
[variant]="variant"
|
|
240
|
-
[nested]="nested"
|
|
241
|
-
[expanded]="expandedSet.has(i)"
|
|
242
|
-
(expandedChange)="onExpandedChange(i, $event)"
|
|
243
|
-
/>
|
|
244
|
-
}
|
|
231
|
+
args: [{ selector: 'compsych-accordion', standalone: true, imports: [AccordionItemComponent], template: `
|
|
232
|
+
@for (item of items; track $index; let i = $index) {
|
|
233
|
+
<compsych-accordion-item
|
|
234
|
+
[title]="item.title"
|
|
235
|
+
[content]="item.content ?? ''"
|
|
236
|
+
[icon]="item.icon ?? ''"
|
|
237
|
+
[buttons]="item.buttons ?? []"
|
|
238
|
+
[nestedContent]="item.nestedContent ?? []"
|
|
239
|
+
[variant]="variant"
|
|
240
|
+
[nested]="nested"
|
|
241
|
+
[expanded]="expandedSet.has(i)"
|
|
242
|
+
(expandedChange)="onExpandedChange(i, $event)"
|
|
243
|
+
/>
|
|
244
|
+
}
|
|
245
245
|
`, host: {
|
|
246
246
|
'[class.level-2]': 'variant === "level-2"',
|
|
247
247
|
'[class.text-only]': 'variant === "text-only"',
|
|
@@ -1 +1 @@
|
|
|
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})\n/**\n * A single expandable row used internally by `AccordionComponent`.\n * Consumers should use `compsych-accordion` with an `items` array rather\n * than placing `compsych-accordion-item` elements directly.\n */\nexport class AccordionItemComponent {\n readonly triggerId = `accordion-trigger-${nextId}`;\n readonly panelId = `accordion-panel-${nextId++}`;\n\n /** Text shown in the trigger button header. */\n @Input() title = '';\n /** HTML string rendered in the panel body when expanded. Sanitized before display. */\n @Input() content = '';\n /** Action buttons rendered at the bottom of the panel. */\n @Input() buttons: AccordionButton[] = [];\n /** Items rendered as a nested level-2 accordion inside this panel. */\n @Input() nestedContent: NestedAccordionItem[] = [];\n /** Visual style — inherited from the parent AccordionComponent. */\n @Input() variant: AccordionVariant = 'default';\n /** True when this item is rendered inside a parent accordion panel. Adds nested styling. Set automatically by AccordionComponent — do not set directly. */\n @Input() nested = false;\n\n /** Lucide icon name (kebab-case) shown to the left of the title. Pass an empty string to show no icon. */\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 /** Whether this panel is currently open. Supports boolean coercion (e.g. `[expanded]=\"true\"`). */\n @Input()\n get expanded(): boolean { return this._expanded; }\n set expanded(value: BooleanInput) { this._expanded = coerceBooleanProperty(value); }\n private _expanded = false;\n\n /** Emitted when the panel is toggled. `true` = opening, `false` = closing. */\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 /** Items to render as expandable rows. */\n @Input() items: AccordionItem[] = [];\n /** Visual style applied to all items. `'default'` is the standard card style; `'level-2'` is compact nested style; `'text-only'` removes card chrome. */\n @Input() variant: AccordionVariant = 'default';\n /** Set automatically when this accordion is rendered inside another accordion panel. Do not set directly. */\n @Input() nested = false;\n /** When true, multiple panels can be open simultaneously. Defaults to false — opening one panel closes any others. Supports boolean coercion. */\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
|
+
{"version":3,"file":"accordion.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/accordion/accordion.ts"],"names":[],"mappings":"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","sourcesContent":["import {\r\n Component,\r\n ElementRef,\r\n EventEmitter,\r\n forwardRef,\r\n HostListener,\r\n Input,\r\n Output,\r\n SecurityContext,\r\n} from '@angular/core';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { BooleanInput, coerceBooleanProperty } from '@angular/cdk/coercion';\r\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\r\nimport { ButtonComponent } from '../button/button';\r\nimport { AccordionButton, AccordionItem, AccordionVariant, NestedAccordionItem } from './accordion-types';\r\n\r\nexport type { AccordionButton, AccordionItem, AccordionVariant, NestedAccordionItem };\r\n\r\nlet nextId = 0;\r\n\r\n// AccordionItemComponent is defined before AccordionComponent because AccordionComponent\r\n// uses it directly in its imports. AccordionItemComponent references AccordionComponent\r\n// via forwardRef to break the forward-reference cycle within this file.\r\n@Component({\r\n selector: 'compsych-accordion-item',\r\n standalone: true,\r\n imports: [ButtonComponent, forwardRef(() => AccordionComponent)],\r\n template: `\r\n <button\r\n class=\"trigger\"\r\n type=\"button\"\r\n [id]=\"triggerId\"\r\n [attr.aria-expanded]=\"expanded\"\r\n [attr.aria-controls]=\"panelId\"\r\n (click)=\"toggle()\"\r\n >\r\n @if (iconSvg) {\r\n <span class=\"trigger-leading-icon\" [innerHTML]=\"iconSvg\" aria-hidden=\"true\"></span>\r\n }\r\n <span class=\"trigger-label\">{{ title }}</span>\r\n <span class=\"trigger-toggle material-symbols-rounded\" aria-hidden=\"true\">{{ expanded ? 'remove' : 'add' }}</span>\r\n </button>\r\n <div\r\n [id]=\"panelId\"\r\n role=\"region\"\r\n [attr.aria-labelledby]=\"triggerId\"\r\n [hidden]=\"!expanded\"\r\n >\r\n @if (content) {\r\n <div class=\"content\" [innerHTML]=\"sanitizedContent\"></div>\r\n }\r\n @if (nestedContent.length) {\r\n <compsych-accordion [items]=\"nestedContent\" variant=\"level-2\" [nested]=\"true\" />\r\n }\r\n @if (buttons.length) {\r\n <div class=\"actions\">\r\n @for (btn of buttons; track btn.label) {\r\n <compsych-button [label]=\"btn.label\" variant=\"filled\" size=\"small\" (click)=\"btn.action()\" />\r\n }\r\n </div>\r\n }\r\n </div>\r\n `,\r\n styleUrl: './accordion-item.css',\r\n host: {\r\n class: 'compsych-accordion-item',\r\n '[class.level-2]': 'variant === \"level-2\"',\r\n '[class.text-only]': 'variant === \"text-only\"',\r\n '[class.nested]': 'nested',\r\n },\r\n})\r\n/**\r\n * A single expandable row used internally by `AccordionComponent`.\r\n * Consumers should use `compsych-accordion` with an `items` array rather\r\n * than placing `compsych-accordion-item` elements directly.\r\n */\r\nexport class AccordionItemComponent {\r\n readonly triggerId = `accordion-trigger-${nextId}`;\r\n readonly panelId = `accordion-panel-${nextId++}`;\r\n\r\n /** Text shown in the trigger button header. */\r\n @Input() title = '';\r\n /** HTML string rendered in the panel body when expanded. Sanitized before display. */\r\n @Input() content = '';\r\n /** Action buttons rendered at the bottom of the panel. */\r\n @Input() buttons: AccordionButton[] = [];\r\n /** Items rendered as a nested level-2 accordion inside this panel. */\r\n @Input() nestedContent: NestedAccordionItem[] = [];\r\n /** Visual style — inherited from the parent AccordionComponent. */\r\n @Input() variant: AccordionVariant = 'default';\r\n /** True when this item is rendered inside a parent accordion panel. Adds nested styling. Set automatically by AccordionComponent — do not set directly. */\r\n @Input() nested = false;\r\n\r\n /** Lucide icon name (kebab-case) shown to the left of the title. Pass an empty string to show no icon. */\r\n @Input()\r\n set icon(name: string) {\r\n const svg = LUCIDE_ICONS[name];\r\n this.iconSvg = svg ? this.sanitizer.bypassSecurityTrustHtml(svg) : null;\r\n }\r\n\r\n iconSvg: SafeHtml | null = null;\r\n\r\n get sanitizedContent(): SafeHtml {\r\n return this.sanitizer.sanitize(SecurityContext.HTML, this.content) ?? '';\r\n }\r\n\r\n constructor(\r\n private readonly sanitizer: DomSanitizer,\r\n private readonly el: ElementRef<HTMLElement>,\r\n ) {}\r\n\r\n /** Whether this panel is currently open. Supports boolean coercion (e.g. `[expanded]=\"true\"`). */\r\n @Input()\r\n get expanded(): boolean { return this._expanded; }\r\n set expanded(value: BooleanInput) { this._expanded = coerceBooleanProperty(value); }\r\n private _expanded = false;\r\n\r\n /** Emitted when the panel is toggled. `true` = opening, `false` = closing. */\r\n @Output() expandedChange = new EventEmitter<boolean>();\r\n\r\n toggle(): void {\r\n this._expanded = !this._expanded;\r\n this.expandedChange.emit(this._expanded);\r\n }\r\n\r\n @HostListener('keydown', ['$event'])\r\n onKeydown(event: KeyboardEvent): void {\r\n const target = event.target as HTMLElement;\r\n if (!(target instanceof HTMLButtonElement) || !target.classList.contains('trigger')) return;\r\n\r\n const parent = this.el.nativeElement.parentElement;\r\n if (!parent) return;\r\n\r\n const triggers = Array.from(\r\n parent.querySelectorAll<HTMLButtonElement>(':scope > compsych-accordion-item > button.trigger'),\r\n );\r\n const currentIndex = triggers.indexOf(target);\r\n let focusIndex = -1;\r\n\r\n switch (event.key) {\r\n case 'ArrowDown': focusIndex = currentIndex + 1; break;\r\n case 'ArrowUp': focusIndex = currentIndex - 1; break;\r\n case 'Home': focusIndex = 0; break;\r\n case 'End': focusIndex = triggers.length - 1; break;\r\n default: return;\r\n }\r\n\r\n if (focusIndex >= 0 && focusIndex < triggers.length) {\r\n triggers[focusIndex].focus();\r\n event.preventDefault();\r\n }\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'compsych-accordion',\r\n standalone: true,\r\n imports: [AccordionItemComponent],\r\n template: `\r\n @for (item of items; track $index; let i = $index) {\r\n <compsych-accordion-item\r\n [title]=\"item.title\"\r\n [content]=\"item.content ?? ''\"\r\n [icon]=\"item.icon ?? ''\"\r\n [buttons]=\"item.buttons ?? []\"\r\n [nestedContent]=\"item.nestedContent ?? []\"\r\n [variant]=\"variant\"\r\n [nested]=\"nested\"\r\n [expanded]=\"expandedSet.has(i)\"\r\n (expandedChange)=\"onExpandedChange(i, $event)\"\r\n />\r\n }\r\n `,\r\n styleUrl: './accordion.css',\r\n host: {\r\n '[class.level-2]': 'variant === \"level-2\"',\r\n '[class.text-only]': 'variant === \"text-only\"',\r\n '[class.nested]': 'nested',\r\n },\r\n})\r\nexport class AccordionComponent {\r\n /** Items to render as expandable rows. */\r\n @Input() items: AccordionItem[] = [];\r\n /** Visual style applied to all items. `'default'` is the standard card style; `'level-2'` is compact nested style; `'text-only'` removes card chrome. */\r\n @Input() variant: AccordionVariant = 'default';\r\n /** Set automatically when this accordion is rendered inside another accordion panel. Do not set directly. */\r\n @Input() nested = false;\r\n /** When true, multiple panels can be open simultaneously. Defaults to false — opening one panel closes any others. Supports boolean coercion. */\r\n @Input()\r\n get multi(): boolean { return this._multi; }\r\n set multi(value: BooleanInput) { this._multi = coerceBooleanProperty(value); }\r\n private _multi = false;\r\n\r\n expandedSet = new Set<number>();\r\n\r\n onExpandedChange(index: number, isExpanded: boolean): void {\r\n if (isExpanded) {\r\n if (!this._multi) this.expandedSet.clear();\r\n this.expandedSet.add(index);\r\n } else {\r\n this.expandedSet.delete(index);\r\n }\r\n this.expandedSet = new Set(this.expandedSet);\r\n }\r\n}\r\n"]}
|
|
@@ -1,88 +1,111 @@
|
|
|
1
|
-
import { Component,
|
|
2
|
-
import { FocusMonitor } from '@angular/cdk/a11y';
|
|
3
|
-
import { coerceBooleanProperty } from '@angular/cdk/coercion';
|
|
1
|
+
import { Component, Input, booleanAttribute } from '@angular/core';
|
|
4
2
|
import * as i0 from "@angular/core";
|
|
3
|
+
/**
|
|
4
|
+
* A themeable button that renders a native `<button>` element, so it is fully
|
|
5
|
+
* form-submittable and keyboard/screen-reader accessible out of the box.
|
|
6
|
+
*/
|
|
5
7
|
export class ButtonComponent {
|
|
6
|
-
focusMonitor = inject(FocusMonitor);
|
|
7
|
-
elementRef = inject((ElementRef));
|
|
8
8
|
/** Text label rendered on the button. */
|
|
9
9
|
label = '';
|
|
10
10
|
/** Visual style of the button. Defaults to `'filled'`. */
|
|
11
11
|
variant = 'filled';
|
|
12
12
|
/** Button dimensions — controls height, padding, and font size. Defaults to `'small'`. */
|
|
13
13
|
size = 'small';
|
|
14
|
-
/** Material Symbols icon name
|
|
14
|
+
/** Material Symbols icon name (e.g. `'search'`, `'arrow_forward'`). Omit to show label only. */
|
|
15
15
|
icon;
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
/** Placement of `icon` relative to the label. Defaults to `'leading'`. */
|
|
17
|
+
iconPosition = 'leading';
|
|
18
|
+
/** Native button type. Use `'submit'` inside a `<form>` to submit it. Defaults to `'button'`. */
|
|
19
|
+
type = 'button';
|
|
20
|
+
/**
|
|
21
|
+
* Accessible name. Required for icon-only buttons since the icon is decorative
|
|
22
|
+
* (`aria-hidden`). When `iconOnly` is set and this is omitted, `label` is used.
|
|
23
|
+
*/
|
|
24
|
+
ariaLabel;
|
|
25
|
+
/** Hides the label and renders a square icon-only button. Requires `icon`. Supports boolean coercion. */
|
|
26
|
+
iconOnly = false;
|
|
27
|
+
/** Disables the button — non-interactive and dimmed. Supports boolean coercion. */
|
|
28
|
+
disabled = false;
|
|
29
|
+
/**
|
|
30
|
+
* Loading state. Replaces the content with a centered spinner while preserving
|
|
31
|
+
* the button's width, blocks re-clicks, and exposes `aria-busy="true"`. The
|
|
32
|
+
* variant's colours are retained (unlike `disabled`, which dims the button).
|
|
33
|
+
* Supports boolean coercion.
|
|
34
|
+
*/
|
|
35
|
+
loading = false;
|
|
36
|
+
/** Space-separated class list applied to the inner native button. */
|
|
37
|
+
get buttonClasses() {
|
|
30
38
|
return [
|
|
39
|
+
'cmp-btn',
|
|
31
40
|
this.variant,
|
|
32
41
|
this.size,
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
35
|
-
]
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return this._disabled ? 'true' : null;
|
|
42
|
+
this.iconOnly ? 'icon-only' : '',
|
|
43
|
+
this.loading ? 'loading' : '',
|
|
44
|
+
]
|
|
45
|
+
.filter(Boolean)
|
|
46
|
+
.join(' ');
|
|
39
47
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (!this._disabled) {
|
|
45
|
-
event.preventDefault();
|
|
46
|
-
this.elementRef.nativeElement.click();
|
|
48
|
+
/** Resolves the accessible name, guaranteeing icon-only and loading buttons are labelled. */
|
|
49
|
+
get computedAriaLabel() {
|
|
50
|
+
if (this.ariaLabel) {
|
|
51
|
+
return this.ariaLabel;
|
|
47
52
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
event.preventDefault();
|
|
52
|
-
}
|
|
53
|
-
onSpaceKeyup() {
|
|
54
|
-
if (!this._disabled) {
|
|
55
|
-
this.elementRef.nativeElement.click();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
ngAfterViewInit() {
|
|
59
|
-
this.focusMonitor.monitor(this.elementRef).subscribe(origin => {
|
|
60
|
-
this._focused = !!origin;
|
|
61
|
-
this._focusOrigin = origin;
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
ngOnDestroy() {
|
|
65
|
-
this.focusMonitor.stopMonitoring(this.elementRef);
|
|
53
|
+
// Icon-only buttons have no visible text; while loading the label is hidden from the
|
|
54
|
+
// a11y tree. In both cases fall back to `label` so the button keeps an accessible name.
|
|
55
|
+
return this.iconOnly || this.loading ? this.label || null : null;
|
|
66
56
|
}
|
|
67
57
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ButtonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
68
|
-
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",
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
58
|
+
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", iconPosition: "iconPosition", type: "type", ariaLabel: "ariaLabel", iconOnly: ["iconOnly", "iconOnly", booleanAttribute], disabled: ["disabled", "disabled", booleanAttribute], loading: ["loading", "loading", booleanAttribute] }, ngImport: i0, template: `
|
|
59
|
+
<button
|
|
60
|
+
[class]="buttonClasses"
|
|
61
|
+
[type]="type"
|
|
62
|
+
[disabled]="disabled || loading"
|
|
63
|
+
[attr.aria-busy]="loading ? 'true' : null"
|
|
64
|
+
[attr.aria-label]="computedAriaLabel"
|
|
65
|
+
>
|
|
66
|
+
@if (icon && (iconOnly || iconPosition === 'leading')) {
|
|
67
|
+
<span class="icon material-symbols-rounded" aria-hidden="true">{{ icon }}</span>
|
|
68
|
+
}
|
|
69
|
+
@if (!iconOnly) {
|
|
70
|
+
<span class="label">{{ label }}</span>
|
|
71
|
+
}
|
|
72
|
+
@if (icon && !iconOnly && iconPosition === 'trailing') {
|
|
73
|
+
<span class="icon material-symbols-rounded" aria-hidden="true">{{ icon }}</span>
|
|
74
|
+
}
|
|
75
|
+
@if (loading) {
|
|
76
|
+
<span class="spinner-overlay" aria-hidden="true">
|
|
77
|
+
<span class="icon spinner material-symbols-rounded">progress_activity</span>
|
|
78
|
+
</span>
|
|
79
|
+
}
|
|
80
|
+
</button>
|
|
81
|
+
`, isInline: true, styles: [":host{display:inline-flex}.cmp-btn{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;margin:0;border:none;border-radius:var(--border-radius-sys-radius-full);background:none;color:inherit;font:inherit;cursor:pointer;white-space:nowrap;position:relative;overflow:hidden;appearance:none;-webkit-appearance:none}.cmp-btn:before{content:\"\";position:absolute;inset:0;border-radius:inherit;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}.cmp-btn:hover:before{opacity:.08}.cmp-btn:active:before{opacity:.1}.cmp-btn:focus-visible{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)}.cmp-btn:focus-visible:before{opacity:.12}.cmp-btn:disabled{cursor:not-allowed;pointer-events:none}.cmp-btn:disabled:not(.loading){opacity:.38}.icon{font-size:inherit;line-height:1;-webkit-user-select:none;user-select:none}.cmp-btn.icon-only{padding:0;aspect-ratio:1}.cmp-btn.icon-only.small{width:2rem}.cmp-btn.icon-only.medium{width:2.5rem}.cmp-btn.icon-only.large{width:3rem}.cmp-btn.icon-only.xlarge{width:3.5rem}.cmp-btn.loading>:not(.spinner-overlay){visibility:hidden}.spinner-overlay{position:absolute;inset:0;display:inline-flex;align-items:center;justify-content:center}.spinner{animation:cmp-btn-spin 1s linear infinite}@keyframes cmp-btn-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinner{animation-duration:2.4s}}.cmp-btn.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)}.cmp-btn.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)}.cmp-btn.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)}.cmp-btn.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)}.cmp-btn.filled{background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary)}.cmp-btn.tonal{background-color:var(--add-on-primary-fixed-sys-primary-fixed-dim);color:var(--add-on-primary-fixed-sys-on-primary-fixed)}.cmp-btn.outlined{background-color:transparent;color:var(--surface-surface-sys-on-surface);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}.cmp-btn.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)}.cmp-btn.text{background-color:transparent;color:var(--surface-surface-sys-on-surface);padding-inline:0}.cmp-btn.danger{background-color:var(--error-sys-error);color:var(--error-sys-on-error)}.cmp-btn.danger-outlined{background-color:transparent;color:var(--error-sys-error);border:var(--border-width-sys-stroke-thin) solid var(--error-sys-error-container)}@media(forced-colors:active){.cmp-btn{border:1px solid ButtonText;forced-color-adjust:none}.cmp-btn.filled,.cmp-btn.tonal,.cmp-btn.danger{background:ButtonText;color:ButtonFace}.cmp-btn:focus-visible{outline:2px solid Highlight;outline-offset:2px}.cmp-btn:disabled:not(.loading){color:GrayText;border-color:GrayText}}\n"] });
|
|
74
82
|
}
|
|
75
83
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: ButtonComponent, decorators: [{
|
|
76
84
|
type: Component,
|
|
77
85
|
args: [{ selector: 'compsych-button', standalone: true, template: `
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
<button
|
|
87
|
+
[class]="buttonClasses"
|
|
88
|
+
[type]="type"
|
|
89
|
+
[disabled]="disabled || loading"
|
|
90
|
+
[attr.aria-busy]="loading ? 'true' : null"
|
|
91
|
+
[attr.aria-label]="computedAriaLabel"
|
|
92
|
+
>
|
|
93
|
+
@if (icon && (iconOnly || iconPosition === 'leading')) {
|
|
94
|
+
<span class="icon material-symbols-rounded" aria-hidden="true">{{ icon }}</span>
|
|
95
|
+
}
|
|
96
|
+
@if (!iconOnly) {
|
|
97
|
+
<span class="label">{{ label }}</span>
|
|
98
|
+
}
|
|
99
|
+
@if (icon && !iconOnly && iconPosition === 'trailing') {
|
|
100
|
+
<span class="icon material-symbols-rounded" aria-hidden="true">{{ icon }}</span>
|
|
101
|
+
}
|
|
102
|
+
@if (loading) {
|
|
103
|
+
<span class="spinner-overlay" aria-hidden="true">
|
|
104
|
+
<span class="icon spinner material-symbols-rounded">progress_activity</span>
|
|
105
|
+
</span>
|
|
106
|
+
}
|
|
107
|
+
</button>
|
|
108
|
+
`, styles: [":host{display:inline-flex}.cmp-btn{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;margin:0;border:none;border-radius:var(--border-radius-sys-radius-full);background:none;color:inherit;font:inherit;cursor:pointer;white-space:nowrap;position:relative;overflow:hidden;appearance:none;-webkit-appearance:none}.cmp-btn:before{content:\"\";position:absolute;inset:0;border-radius:inherit;background-color:currentColor;opacity:0;transition:opacity .15s ease;pointer-events:none}.cmp-btn:hover:before{opacity:.08}.cmp-btn:active:before{opacity:.1}.cmp-btn:focus-visible{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)}.cmp-btn:focus-visible:before{opacity:.12}.cmp-btn:disabled{cursor:not-allowed;pointer-events:none}.cmp-btn:disabled:not(.loading){opacity:.38}.icon{font-size:inherit;line-height:1;-webkit-user-select:none;user-select:none}.cmp-btn.icon-only{padding:0;aspect-ratio:1}.cmp-btn.icon-only.small{width:2rem}.cmp-btn.icon-only.medium{width:2.5rem}.cmp-btn.icon-only.large{width:3rem}.cmp-btn.icon-only.xlarge{width:3.5rem}.cmp-btn.loading>:not(.spinner-overlay){visibility:hidden}.spinner-overlay{position:absolute;inset:0;display:inline-flex;align-items:center;justify-content:center}.spinner{animation:cmp-btn-spin 1s linear infinite}@keyframes cmp-btn-spin{to{transform:rotate(360deg)}}@media(prefers-reduced-motion:reduce){.spinner{animation-duration:2.4s}}.cmp-btn.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)}.cmp-btn.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)}.cmp-btn.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)}.cmp-btn.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)}.cmp-btn.filled{background-color:var(--accent-primary-sys-primary);color:var(--accent-primary-sys-on-primary)}.cmp-btn.tonal{background-color:var(--add-on-primary-fixed-sys-primary-fixed-dim);color:var(--add-on-primary-fixed-sys-on-primary-fixed)}.cmp-btn.outlined{background-color:transparent;color:var(--surface-surface-sys-on-surface);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}.cmp-btn.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)}.cmp-btn.text{background-color:transparent;color:var(--surface-surface-sys-on-surface);padding-inline:0}.cmp-btn.danger{background-color:var(--error-sys-error);color:var(--error-sys-on-error)}.cmp-btn.danger-outlined{background-color:transparent;color:var(--error-sys-error);border:var(--border-width-sys-stroke-thin) solid var(--error-sys-error-container)}@media(forced-colors:active){.cmp-btn{border:1px solid ButtonText;forced-color-adjust:none}.cmp-btn.filled,.cmp-btn.tonal,.cmp-btn.danger{background:ButtonText;color:ButtonFace}.cmp-btn:focus-visible{outline:2px solid Highlight;outline-offset:2px}.cmp-btn:disabled:not(.loading){color:GrayText;border-color:GrayText}}\n"] }]
|
|
86
109
|
}], propDecorators: { label: [{
|
|
87
110
|
type: Input
|
|
88
111
|
}], variant: [{
|
|
@@ -91,27 +114,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
|
|
|
91
114
|
type: Input
|
|
92
115
|
}], icon: [{
|
|
93
116
|
type: Input
|
|
94
|
-
}],
|
|
117
|
+
}], iconPosition: [{
|
|
95
118
|
type: Input
|
|
96
|
-
}],
|
|
119
|
+
}], type: [{
|
|
97
120
|
type: Input
|
|
98
|
-
}],
|
|
99
|
-
type:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
args: ['keydown.enter', ['$event']]
|
|
110
|
-
}], onSpaceKeydown: [{
|
|
111
|
-
type: HostListener,
|
|
112
|
-
args: ['keydown.space', ['$event']]
|
|
113
|
-
}], onSpaceKeyup: [{
|
|
114
|
-
type: HostListener,
|
|
115
|
-
args: ['keyup.space']
|
|
121
|
+
}], ariaLabel: [{
|
|
122
|
+
type: Input
|
|
123
|
+
}], iconOnly: [{
|
|
124
|
+
type: Input,
|
|
125
|
+
args: [{ transform: booleanAttribute }]
|
|
126
|
+
}], disabled: [{
|
|
127
|
+
type: Input,
|
|
128
|
+
args: [{ transform: booleanAttribute }]
|
|
129
|
+
}], loading: [{
|
|
130
|
+
type: Input,
|
|
131
|
+
args: [{ transform: booleanAttribute }]
|
|
116
132
|
}] } });
|
|
117
133
|
//# sourceMappingURL=button.js.map
|