@compsych-ui-components/angular 7.0.9 → 7.0.11

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
@@ -7,6 +7,7 @@ import { SidebarComponent } from './lib/sidebar/sidebar';
7
7
  import { CardComponent } from './lib/card-base/card';
8
8
  import { CardGridComponent } from './lib/card-grid/card-grid';
9
9
  import { TextInputComponent } from './lib/text-input/text-input';
10
+ import { DialogComponent } from './lib/dialog/dialog';
10
11
  import * as i0 from "@angular/core";
11
12
  export { ButtonComponent } from './lib/button/button';
12
13
  export { AccordionComponent } from './lib/accordion/accordion';
@@ -17,6 +18,11 @@ export { CardComponent } from './lib/card-base/card';
17
18
  export { RippleDirective } from './lib/card-base/ripple.directive';
18
19
  export { CardGridComponent } from './lib/card-grid/card-grid';
19
20
  export { TextInputComponent } from './lib/text-input/text-input';
21
+ export { DialogComponent } from './lib/dialog/dialog';
22
+ export { DialogService } from './lib/dialog/dialog.service';
23
+ export { CompsychDialogRef } from './lib/dialog/dialog-ref';
24
+ // Re-exported from CDK so opened content components can inject the data token.
25
+ export { DIALOG_DATA } from '@angular/cdk/dialog';
20
26
  export class CompsychAngularModule {
21
27
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
22
28
  static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule, imports: [ButtonComponent,
@@ -26,14 +32,16 @@ export class CompsychAngularModule {
26
32
  SidebarComponent,
27
33
  CardComponent,
28
34
  CardGridComponent,
29
- TextInputComponent], exports: [ButtonComponent,
35
+ TextInputComponent,
36
+ DialogComponent], exports: [ButtonComponent,
30
37
  AccordionComponent,
31
38
  FooterComponent,
32
39
  HeaderNavComponent,
33
40
  SidebarComponent,
34
41
  CardComponent,
35
42
  CardGridComponent,
36
- TextInputComponent] });
43
+ TextInputComponent,
44
+ DialogComponent] });
37
45
  static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule });
38
46
  }
39
47
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CompsychAngularModule, decorators: [{
@@ -48,6 +56,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
48
56
  CardComponent,
49
57
  CardGridComponent,
50
58
  TextInputComponent,
59
+ DialogComponent,
51
60
  ],
52
61
  exports: [
53
62
  ButtonComponent,
@@ -58,6 +67,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
58
67
  CardComponent,
59
68
  CardGridComponent,
60
69
  TextInputComponent,
70
+ DialogComponent,
61
71
  ],
62
72
  }]
63
73
  }] });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/angular/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;;AAEjE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAyBjE,MAAM,OAAO,qBAAqB;wGAArB,qBAAqB;yGAArB,qBAAqB,YApB9B,eAAe;YACf,kBAAkB;YAClB,eAAe;YACf,kBAAkB;YAClB,gBAAgB;YAChB,aAAa;YACb,iBAAiB;YACjB,kBAAkB,aAGlB,eAAe;YACf,kBAAkB;YAClB,eAAe;YACf,kBAAkB;YAClB,gBAAgB;YAChB,aAAa;YACb,iBAAiB;YACjB,kBAAkB;yGAGT,qBAAqB;;4FAArB,qBAAqB;kBAtBjC,QAAQ;mBAAC;oBACR,OAAO,EAAE;wBACP,eAAe;wBACf,kBAAkB;wBAClB,eAAe;wBACf,kBAAkB;wBAClB,gBAAgB;wBAChB,aAAa;wBACb,iBAAiB;wBACjB,kBAAkB;qBACnB;oBACD,OAAO,EAAE;wBACP,eAAe;wBACf,kBAAkB;wBAClB,eAAe;wBACf,kBAAkB;wBAClB,gBAAgB;wBAChB,aAAa;wBACb,iBAAiB;wBACjB,kBAAkB;qBACnB;iBACF","sourcesContent":["import { NgModule } from '@angular/core';\r\nimport { ButtonComponent } from './lib/button/button';\r\nimport { AccordionComponent } from './lib/accordion/accordion';\r\nimport { FooterComponent } from './lib/footer/footer';\r\nimport { HeaderNavComponent } from './lib/header-nav/header-nav';\r\nimport { SidebarComponent } from './lib/sidebar/sidebar';\r\nimport { CardComponent } from './lib/card-base/card';\r\nimport { CardGridComponent } from './lib/card-grid/card-grid';\r\nimport { TextInputComponent } from './lib/text-input/text-input';\r\n\r\nexport { ButtonComponent } from './lib/button/button';\r\nexport type { ButtonVariant, ButtonSize, ButtonType } from './lib/button/button';\r\nexport { AccordionComponent } from './lib/accordion/accordion';\r\nexport type { AccordionItem, AccordionButton, AccordionVariant, NestedAccordionItem } from './lib/accordion/accordion-types';\r\nexport { FooterComponent } from './lib/footer/footer';\r\nexport type { FooterLink } from './lib/footer/footer';\r\nexport { HeaderNavComponent } from './lib/header-nav/header-nav';\r\nexport type { HeaderNavLevel, NavAction } from './lib/header-nav/header-nav';\r\nexport { SidebarComponent } from './lib/sidebar/sidebar';\r\nexport type { SidebarItem } from './lib/sidebar/sidebar';\r\nexport { CardComponent } from './lib/card-base/card';\r\nexport type { CardVariant, CardAppearance, CardRadius, CardPadding, CardOrientation, CardHeadingLevel } from './lib/card-base/card';\r\nexport { RippleDirective } from './lib/card-base/ripple.directive';\r\nexport { CardGridComponent } from './lib/card-grid/card-grid';\r\nexport type { CardGridLayout } from './lib/card-grid/card-grid';\r\nexport { TextInputComponent } from './lib/text-input/text-input';\r\nexport type { TextInputSize, TextInputType } from './lib/text-input/text-input';\r\n\r\n@NgModule({\r\n imports: [\r\n ButtonComponent,\r\n AccordionComponent,\r\n FooterComponent,\r\n HeaderNavComponent,\r\n SidebarComponent,\r\n CardComponent,\r\n CardGridComponent,\r\n TextInputComponent,\r\n ],\r\n exports: [\r\n ButtonComponent,\r\n AccordionComponent,\r\n FooterComponent,\r\n HeaderNavComponent,\r\n SidebarComponent,\r\n CardComponent,\r\n CardGridComponent,\r\n TextInputComponent,\r\n ],\r\n})\r\nexport class CompsychAngularModule {}\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/angular/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;AAEtD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAEjE,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,+EAA+E;AAC/E,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AA0BlD,MAAM,OAAO,qBAAqB;wGAArB,qBAAqB;yGAArB,qBAAqB,YAtB9B,eAAe;YACf,kBAAkB;YAClB,eAAe;YACf,kBAAkB;YAClB,gBAAgB;YAChB,aAAa;YACb,iBAAiB;YACjB,kBAAkB;YAClB,eAAe,aAGf,eAAe;YACf,kBAAkB;YAClB,eAAe;YACf,kBAAkB;YAClB,gBAAgB;YAChB,aAAa;YACb,iBAAiB;YACjB,kBAAkB;YAClB,eAAe;yGAGN,qBAAqB;;4FAArB,qBAAqB;kBAxBjC,QAAQ;mBAAC;oBACR,OAAO,EAAE;wBACP,eAAe;wBACf,kBAAkB;wBAClB,eAAe;wBACf,kBAAkB;wBAClB,gBAAgB;wBAChB,aAAa;wBACb,iBAAiB;wBACjB,kBAAkB;wBAClB,eAAe;qBAChB;oBACD,OAAO,EAAE;wBACP,eAAe;wBACf,kBAAkB;wBAClB,eAAe;wBACf,kBAAkB;wBAClB,gBAAgB;wBAChB,aAAa;wBACb,iBAAiB;wBACjB,kBAAkB;wBAClB,eAAe;qBAChB;iBACF","sourcesContent":["import { NgModule } from '@angular/core';\r\nimport { ButtonComponent } from './lib/button/button';\r\nimport { AccordionComponent } from './lib/accordion/accordion';\r\nimport { FooterComponent } from './lib/footer/footer';\r\nimport { HeaderNavComponent } from './lib/header-nav/header-nav';\r\nimport { SidebarComponent } from './lib/sidebar/sidebar';\r\nimport { CardComponent } from './lib/card-base/card';\r\nimport { CardGridComponent } from './lib/card-grid/card-grid';\r\nimport { TextInputComponent } from './lib/text-input/text-input';\r\nimport { DialogComponent } from './lib/dialog/dialog';\r\n\r\nexport { ButtonComponent } from './lib/button/button';\r\nexport type { ButtonVariant, ButtonSize, ButtonType } from './lib/button/button';\r\nexport { AccordionComponent } from './lib/accordion/accordion';\r\nexport type { AccordionItem, AccordionButton, AccordionVariant, NestedAccordionItem } from './lib/accordion/accordion-types';\r\nexport { FooterComponent } from './lib/footer/footer';\r\nexport type { FooterLink } from './lib/footer/footer';\r\nexport { HeaderNavComponent } from './lib/header-nav/header-nav';\r\nexport type { HeaderNavLevel, NavAction } from './lib/header-nav/header-nav';\r\nexport { SidebarComponent } from './lib/sidebar/sidebar';\r\nexport type { SidebarItem } from './lib/sidebar/sidebar';\r\nexport { CardComponent } from './lib/card-base/card';\r\nexport type { CardVariant, CardAppearance, CardRadius, CardPadding, CardOrientation, CardHeadingLevel } from './lib/card-base/card';\r\nexport { RippleDirective } from './lib/card-base/ripple.directive';\r\nexport { CardGridComponent } from './lib/card-grid/card-grid';\r\nexport type { CardGridLayout } from './lib/card-grid/card-grid';\r\nexport { TextInputComponent } from './lib/text-input/text-input';\r\nexport type { TextInputSize, TextInputType, TextInputLabelButton } from './lib/text-input/text-input';\r\nexport { DialogComponent } from './lib/dialog/dialog';\r\nexport type { DialogCloseReason } from './lib/dialog/dialog';\r\nexport type { DialogVariant } from './lib/dialog/dialog-types';\r\nexport { DialogService } from './lib/dialog/dialog.service';\r\nexport type { CompsychDialogConfig } from './lib/dialog/dialog.service';\r\nexport { CompsychDialogRef } from './lib/dialog/dialog-ref';\r\n// Re-exported from CDK so opened content components can inject the data token.\r\nexport { DIALOG_DATA } from '@angular/cdk/dialog';\r\n\r\n@NgModule({\r\n imports: [\r\n ButtonComponent,\r\n AccordionComponent,\r\n FooterComponent,\r\n HeaderNavComponent,\r\n SidebarComponent,\r\n CardComponent,\r\n CardGridComponent,\r\n TextInputComponent,\r\n DialogComponent,\r\n ],\r\n exports: [\r\n ButtonComponent,\r\n AccordionComponent,\r\n FooterComponent,\r\n HeaderNavComponent,\r\n SidebarComponent,\r\n CardComponent,\r\n CardGridComponent,\r\n TextInputComponent,\r\n DialogComponent,\r\n ],\r\n})\r\nexport class CompsychAngularModule {}\r\n"]}
@@ -0,0 +1,84 @@
1
+ import { ChangeDetectionStrategy, Component, InjectionToken, inject } from '@angular/core';
2
+ import { CdkDialogContainer, DialogConfig, DialogRef } from '@angular/cdk/dialog';
3
+ import { CdkPortalOutlet } from '@angular/cdk/portal';
4
+ import { DialogSurfaceComponent } from './dialog-surface';
5
+ import * as i0 from "@angular/core";
6
+ /**
7
+ * Chrome params injected into `CmpDialogContainer`. Provided via the CDK `container` object
8
+ * form (`{ type, providers }`) rather than spread onto the `DialogConfig` — that avoids
9
+ * clobbering real config fields (e.g. `width`, which CDK also maps onto the overlay pane).
10
+ */
11
+ export const CMP_DIALOG_CHROME = new InjectionToken('CMP_DIALOG_CHROME');
12
+ /**
13
+ * Container used by `DialogService` via CDK `Dialog.open(content, { container })`. Extends
14
+ * the CDK dialog container (focus trap, focus capture/restore, portal outlet) and renders the
15
+ * shared dialog surface around the attached content. The opened content is attached into the
16
+ * surface's body via the `cdkPortalOutlet`; chrome params (title/variant/etc.) ride on the
17
+ * `DialogConfig`. Internal — not part of the public API.
18
+ *
19
+ * Host aria bindings are redeclared here (component host metadata is not inherited) and read
20
+ * from the injected `DialogConfig`, matching `CdkDialogContainer`'s own bindings.
21
+ */
22
+ export class CmpDialogContainer extends CdkDialogContainer {
23
+ /** The dialog config (provided by CDK to the container injector); drives the aria host bindings. */
24
+ config = inject(DialogConfig);
25
+ /** Ref for the open dialog (provided by CDK to the container injector); closes on the ✕. */
26
+ dialogRef = inject(DialogRef);
27
+ /** Chrome params supplied by `DialogService` via the container `providers`. */
28
+ chrome = inject(CMP_DIALOG_CHROME);
29
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CmpDialogContainer, deps: null, target: i0.ɵɵFactoryTarget.Component });
30
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.14", type: CmpDialogContainer, isStandalone: true, selector: "cmp-dialog-container", host: { attributes: { "tabindex": "-1" }, properties: { "attr.role": "config.role", "attr.aria-modal": "config.ariaModal", "id": "config.id", "attr.aria-labelledby": "config.ariaLabel ? null : config.ariaLabelledBy", "attr.aria-label": "config.ariaLabel", "attr.aria-describedby": "config.ariaDescribedBy" }, classAttribute: "cmp-dialog-container" }, usesInheritance: true, ngImport: i0, template: `
31
+ <cmp-dialog-surface
32
+ [variant]="chrome.variant"
33
+ [width]="chrome.width"
34
+ [title]="chrome.title"
35
+ [description]="chrome.description"
36
+ [icon]="chrome.icon"
37
+ [dismissible]="chrome.dismissible"
38
+ [closeLabel]="chrome.closeLabel"
39
+ [showActions]="chrome.showActions"
40
+ [titleId]="chrome.titleId"
41
+ [descId]="chrome.descId"
42
+ (closeClick)="dialogRef.close()"
43
+ >
44
+ <ng-template cdkPortalOutlet></ng-template>
45
+ </cmp-dialog-surface>
46
+ `, isInline: true, dependencies: [{ kind: "component", type: DialogSurfaceComponent, selector: "cmp-dialog-surface", inputs: ["variant", "width", "title", "description", "icon", "dismissible", "closeLabel", "showActions", "titleId", "descId"], outputs: ["closeClick"] }, { kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.Eager });
47
+ }
48
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: CmpDialogContainer, decorators: [{
49
+ type: Component,
50
+ args: [{
51
+ selector: 'cmp-dialog-container',
52
+ standalone: true,
53
+ changeDetection: ChangeDetectionStrategy.Default,
54
+ imports: [DialogSurfaceComponent, CdkPortalOutlet],
55
+ template: `
56
+ <cmp-dialog-surface
57
+ [variant]="chrome.variant"
58
+ [width]="chrome.width"
59
+ [title]="chrome.title"
60
+ [description]="chrome.description"
61
+ [icon]="chrome.icon"
62
+ [dismissible]="chrome.dismissible"
63
+ [closeLabel]="chrome.closeLabel"
64
+ [showActions]="chrome.showActions"
65
+ [titleId]="chrome.titleId"
66
+ [descId]="chrome.descId"
67
+ (closeClick)="dialogRef.close()"
68
+ >
69
+ <ng-template cdkPortalOutlet></ng-template>
70
+ </cmp-dialog-surface>
71
+ `,
72
+ host: {
73
+ class: 'cmp-dialog-container',
74
+ tabindex: '-1',
75
+ '[attr.role]': 'config.role',
76
+ '[attr.aria-modal]': 'config.ariaModal',
77
+ '[id]': 'config.id',
78
+ '[attr.aria-labelledby]': 'config.ariaLabel ? null : config.ariaLabelledBy',
79
+ '[attr.aria-label]': 'config.ariaLabel',
80
+ '[attr.aria-describedby]': 'config.ariaDescribedBy',
81
+ },
82
+ }]
83
+ }] });
84
+ //# sourceMappingURL=dialog-container.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog-container.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/dialog/dialog-container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;;AAiB1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,cAAc,CAAe,mBAAmB,CAAC,CAAC;AAEvF;;;;;;;;;GASG;AAkCH,MAAM,OAAO,kBAAmB,SAAQ,kBAAkB;IACxD,oGAAoG;IACjF,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IACjD,4FAA4F;IACzE,SAAS,GAAG,MAAM,CAA8B,SAAS,CAAC,CAAC;IAC9E,+EAA+E;IAC5D,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;wGAN3C,kBAAkB;4FAAlB,kBAAkB,scA5BnB;;;;;;;;;;;;;;;;GAgBT,4DAjBS,sBAAsB,uNAAE,eAAe;;4FA6BtC,kBAAkB;kBAjC9B,SAAS;mBAAC;oBACT,QAAQ,EAAE,sBAAsB;oBAChC,UAAU,EAAE,IAAI;oBAChB,eAAe,EAAE,uBAAuB,CAAC,OAAO;oBAChD,OAAO,EAAE,CAAC,sBAAsB,EAAE,eAAe,CAAC;oBAClD,QAAQ,EAAE;;;;;;;;;;;;;;;;GAgBT;oBACD,IAAI,EAAE;wBACJ,KAAK,EAAE,sBAAsB;wBAC7B,QAAQ,EAAE,IAAI;wBACd,aAAa,EAAE,aAAa;wBAC5B,mBAAmB,EAAE,kBAAkB;wBACvC,MAAM,EAAE,WAAW;wBACnB,wBAAwB,EAAE,iDAAiD;wBAC3E,mBAAmB,EAAE,kBAAkB;wBACvC,yBAAyB,EAAE,wBAAwB;qBACpD;iBACF","sourcesContent":["import { ChangeDetectionStrategy, Component, InjectionToken, inject } from '@angular/core';\r\nimport { CdkDialogContainer, DialogConfig, DialogRef } from '@angular/cdk/dialog';\r\nimport { CdkPortalOutlet } from '@angular/cdk/portal';\r\nimport { DialogSurfaceComponent } from './dialog-surface';\r\nimport { DialogVariant } from './dialog-types';\r\n\r\n/** Chrome params supplied by `DialogService` to the container via {@link CMP_DIALOG_CHROME}. */\r\nexport interface DialogChrome {\r\n variant: DialogVariant;\r\n title: string;\r\n description: string;\r\n icon: string;\r\n dismissible: boolean;\r\n closeLabel: string;\r\n width: string;\r\n showActions: boolean;\r\n titleId: string;\r\n descId: string;\r\n}\r\n\r\n/**\r\n * Chrome params injected into `CmpDialogContainer`. Provided via the CDK `container` object\r\n * form (`{ type, providers }`) rather than spread onto the `DialogConfig` — that avoids\r\n * clobbering real config fields (e.g. `width`, which CDK also maps onto the overlay pane).\r\n */\r\nexport const CMP_DIALOG_CHROME = new InjectionToken<DialogChrome>('CMP_DIALOG_CHROME');\r\n\r\n/**\r\n * Container used by `DialogService` via CDK `Dialog.open(content, { container })`. Extends\r\n * the CDK dialog container (focus trap, focus capture/restore, portal outlet) and renders the\r\n * shared dialog surface around the attached content. The opened content is attached into the\r\n * surface's body via the `cdkPortalOutlet`; chrome params (title/variant/etc.) ride on the\r\n * `DialogConfig`. Internal — not part of the public API.\r\n *\r\n * Host aria bindings are redeclared here (component host metadata is not inherited) and read\r\n * from the injected `DialogConfig`, matching `CdkDialogContainer`'s own bindings.\r\n */\r\n@Component({\r\n selector: 'cmp-dialog-container',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.Default,\r\n imports: [DialogSurfaceComponent, CdkPortalOutlet],\r\n template: `\r\n <cmp-dialog-surface\r\n [variant]=\"chrome.variant\"\r\n [width]=\"chrome.width\"\r\n [title]=\"chrome.title\"\r\n [description]=\"chrome.description\"\r\n [icon]=\"chrome.icon\"\r\n [dismissible]=\"chrome.dismissible\"\r\n [closeLabel]=\"chrome.closeLabel\"\r\n [showActions]=\"chrome.showActions\"\r\n [titleId]=\"chrome.titleId\"\r\n [descId]=\"chrome.descId\"\r\n (closeClick)=\"dialogRef.close()\"\r\n >\r\n <ng-template cdkPortalOutlet></ng-template>\r\n </cmp-dialog-surface>\r\n `,\r\n host: {\r\n class: 'cmp-dialog-container',\r\n tabindex: '-1',\r\n '[attr.role]': 'config.role',\r\n '[attr.aria-modal]': 'config.ariaModal',\r\n '[id]': 'config.id',\r\n '[attr.aria-labelledby]': 'config.ariaLabel ? null : config.ariaLabelledBy',\r\n '[attr.aria-label]': 'config.ariaLabel',\r\n '[attr.aria-describedby]': 'config.ariaDescribedBy',\r\n },\r\n})\r\nexport class CmpDialogContainer extends CdkDialogContainer {\r\n /** The dialog config (provided by CDK to the container injector); drives the aria host bindings. */\r\n protected readonly config = inject(DialogConfig);\r\n /** Ref for the open dialog (provided by CDK to the container injector); closes on the ✕. */\r\n protected readonly dialogRef = inject<DialogRef<unknown, unknown>>(DialogRef);\r\n /** Chrome params supplied by `DialogService` via the container `providers`. */\r\n protected readonly chrome = inject(CMP_DIALOG_CHROME);\r\n}\r\n"]}
@@ -0,0 +1,11 @@
1
+ let nextId = 0;
2
+ /**
3
+ * Returns a unique `titleId` / `descId` pair for one dialog instance. Shared by the
4
+ * declarative `compsych-dialog` and the imperative `DialogService` so ids never collide
5
+ * when both kinds of dialog are open at once (e.g. GRO's `ModalService` delegating here).
6
+ */
7
+ export function nextDialogIds() {
8
+ const id = nextId++;
9
+ return { titleId: `cmp-dialog-title-${id}`, descId: `cmp-dialog-desc-${id}` };
10
+ }
11
+ //# sourceMappingURL=dialog-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog-id.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/dialog/dialog-id.ts"],"names":[],"mappings":"AAAA,IAAI,MAAM,GAAG,CAAC,CAAC;AAEf;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,OAAO,EAAE,oBAAoB,EAAE,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE,EAAE,EAAE,CAAC;AAChF,CAAC","sourcesContent":["let nextId = 0;\r\n\r\n/**\r\n * Returns a unique `titleId` / `descId` pair for one dialog instance. Shared by the\r\n * declarative `compsych-dialog` and the imperative `DialogService` so ids never collide\r\n * when both kinds of dialog are open at once (e.g. GRO's `ModalService` delegating here).\r\n */\r\nexport function nextDialogIds(): { titleId: string; descId: string } {\r\n const id = nextId++;\r\n return { titleId: `cmp-dialog-title-${id}`, descId: `cmp-dialog-desc-${id}` };\r\n}\r\n"]}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Reference to a dialog opened via {@link DialogService.open}. Returned to the opener and
3
+ * also injectable by the opened content component/template (to close with a result).
4
+ *
5
+ * Wraps the CDK `DialogRef`. `closed` is a `Promise` that resolves with the close result — or
6
+ * `undefined` when the dialog is dismissed (close button / Escape / backdrop). CDK emits its
7
+ * own `closed` synchronously inside `close()`, which can trigger `ExpressionChangedAfter-
8
+ * ItHasBeenChecked` (NG0100) when a subscriber updates view state; a Promise defers to a
9
+ * microtask, so handlers are change-detection safe. Using a Promise (rather than an rxjs
10
+ * Observable) also keeps the library rxjs-free and mirrors ng-bootstrap's `NgbModalRef.result`.
11
+ */
12
+ export class CompsychDialogRef {
13
+ _cdkRef;
14
+ /** Resolves once the dialog has closed, with the result (or `undefined` when dismissed). */
15
+ closed;
16
+ constructor(_cdkRef) {
17
+ this._cdkRef = _cdkRef;
18
+ this.closed = new Promise((resolve) => {
19
+ this._cdkRef.closed.subscribe((result) => resolve(result));
20
+ });
21
+ }
22
+ /** The opened component instance, or `null` for template content. */
23
+ get componentInstance() {
24
+ return this._cdkRef.componentInstance;
25
+ }
26
+ /** Unique id of the open dialog. */
27
+ get id() {
28
+ return this._cdkRef.id;
29
+ }
30
+ /** Closes the dialog, optionally passing a result to `closed`. */
31
+ close(result) {
32
+ this._cdkRef.close(result);
33
+ }
34
+ }
35
+ //# sourceMappingURL=dialog-ref.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog-ref.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/dialog/dialog-ref.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,iBAAiB;IAIC;IAH7B,4FAA4F;IACnF,MAAM,CAAyB;IAExC,YAA6B,OAA8B;QAA9B,YAAO,GAAP,OAAO,CAAuB;QACzD,IAAI,CAAC,MAAM,GAAG,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE;YACnD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC;IAED,qEAAqE;IACrE,IAAI,iBAAiB;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC;IACxC,CAAC;IAED,oCAAoC;IACpC,IAAI,EAAE;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;IACzB,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,MAAU;QACd,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;CACF","sourcesContent":["import { DialogRef } from '@angular/cdk/dialog';\r\n\r\n/**\r\n * Reference to a dialog opened via {@link DialogService.open}. Returned to the opener and\r\n * also injectable by the opened content component/template (to close with a result).\r\n *\r\n * Wraps the CDK `DialogRef`. `closed` is a `Promise` that resolves with the close result — or\r\n * `undefined` when the dialog is dismissed (close button / Escape / backdrop). CDK emits its\r\n * own `closed` synchronously inside `close()`, which can trigger `ExpressionChangedAfter-\r\n * ItHasBeenChecked` (NG0100) when a subscriber updates view state; a Promise defers to a\r\n * microtask, so handlers are change-detection safe. Using a Promise (rather than an rxjs\r\n * Observable) also keeps the library rxjs-free and mirrors ng-bootstrap's `NgbModalRef.result`.\r\n */\r\nexport class CompsychDialogRef<R = unknown> {\r\n /** Resolves once the dialog has closed, with the result (or `undefined` when dismissed). */\r\n readonly closed: Promise<R | undefined>;\r\n\r\n constructor(private readonly _cdkRef: DialogRef<R, unknown>) {\r\n this.closed = new Promise<R | undefined>((resolve) => {\r\n this._cdkRef.closed.subscribe((result) => resolve(result));\r\n });\r\n }\r\n\r\n /** The opened component instance, or `null` for template content. */\r\n get componentInstance(): unknown {\r\n return this._cdkRef.componentInstance;\r\n }\r\n\r\n /** Unique id of the open dialog. */\r\n get id(): string {\r\n return this._cdkRef.id;\r\n }\r\n\r\n /** Closes the dialog, optionally passing a result to `closed`. */\r\n close(result?: R): void {\r\n this._cdkRef.close(result);\r\n }\r\n}\r\n"]}
@@ -0,0 +1,86 @@
1
+ import { ChangeDetectionStrategy, Component, computed, inject, input, output, } from '@angular/core';
2
+ import { DomSanitizer } from '@angular/platform-browser';
3
+ import { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';
4
+ import * as i0 from "@angular/core";
5
+ /**
6
+ * The backdrop scrim colors the CDK overlay backdrop, which lives outside this component's
7
+ * view encapsulation — so it can't be a scoped stylesheet. The dialog owns it itself (no
8
+ * separate export or consumer import): the first surface to render injects one `<style>`
9
+ * into `<head>`. Idempotent, browser-only. Figma "Scrim/Overlay" (61041:7560): on-surface 10%.
10
+ * Openers set the backdrop's class to `cmp-dialog-scrim`.
11
+ */
12
+ let scrimInjected = false;
13
+ function injectScrimStyles() {
14
+ if (scrimInjected || typeof document === 'undefined') {
15
+ return;
16
+ }
17
+ scrimInjected = true;
18
+ const style = document.createElement('style');
19
+ style.setAttribute('data-cmp-dialog-scrim', '');
20
+ style.textContent =
21
+ '.cmp-dialog-scrim{background-color:' +
22
+ 'color-mix(in srgb, var(--surface-surface-sys-on-surface) 10%, transparent);}';
23
+ document.head.appendChild(style);
24
+ }
25
+ /**
26
+ * Internal presentational chrome for the dialog: the surface box, header (leading icon +
27
+ * title + close, or a floating close), centered icon badge + title/description, the body
28
+ * slot, and the actions footer. Rendered by BOTH the declarative `compsych-dialog` and the
29
+ * imperative `DialogService` container, so the chrome has a single source of truth.
30
+ *
31
+ * Not part of the public API. Overlay/a11y state (role, aria-modal, focus, open) is owned
32
+ * by the host (the CDK dialog container); `titleId`/`descId` are supplied by the host so its
33
+ * `aria-labelledby`/`aria-describedby` can reference the rendered title/description elements.
34
+ * The host renders `display: contents`, so `.cmp-dialog` is the layout box.
35
+ */
36
+ export class DialogSurfaceComponent {
37
+ /** Layout type. */
38
+ variant = input('modal-leading-icon', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
39
+ /** Optional CSS-length width override (falls back to the variant width). */
40
+ width = input('', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
41
+ /** Heading text. */
42
+ title = input('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
43
+ /** Supporting text (rendered for `centered` and `alert` variants). */
44
+ description = input('', ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
45
+ /** Lucide icon name (leading header icon / centered badge glyph). */
46
+ icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
47
+ /** Whether the close button is shown. */
48
+ dismissible = input(true, ...(ngDevMode ? [{ debugName: "dismissible" }] : /* istanbul ignore next */ []));
49
+ /** Accessible label for the close button. */
50
+ closeLabel = input('Close dialog', ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
51
+ /** Whether the projected actions footer is rendered. */
52
+ showActions = input(true, ...(ngDevMode ? [{ debugName: "showActions" }] : /* istanbul ignore next */ []));
53
+ /** Id for the title element (host supplies it for `aria-labelledby`). */
54
+ titleId = input('', ...(ngDevMode ? [{ debugName: "titleId" }] : /* istanbul ignore next */ []));
55
+ /** Id for the description element (host supplies it for `aria-describedby`). */
56
+ descId = input('', ...(ngDevMode ? [{ debugName: "descId" }] : /* istanbul ignore next */ []));
57
+ /** Emitted when the close button is activated. */
58
+ closeClick = output();
59
+ sanitizer = inject(DomSanitizer);
60
+ constructor() {
61
+ // Self-contained scrim: inject the backdrop style on first render (no separate export/import).
62
+ injectScrimStyles();
63
+ }
64
+ /** Header (leading) icon markup at 24px. */
65
+ iconMarkup = computed(() => this.buildIcon(this.icon(), 24), ...(ngDevMode ? [{ debugName: "iconMarkup" }] : /* istanbul ignore next */ []));
66
+ /** Centered badge icon markup at 32px. */
67
+ centeredIconMarkup = computed(() => this.buildIcon(this.icon(), 32), ...(ngDevMode ? [{ debugName: "centeredIconMarkup" }] : /* istanbul ignore next */ []));
68
+ /** Close (`x`) icon markup at 20px. */
69
+ closeMarkup = computed(() => this.buildIcon('x', 20), ...(ngDevMode ? [{ debugName: "closeMarkup" }] : /* istanbul ignore next */ []));
70
+ /**
71
+ * Looks up a Lucide icon by name and injects an explicit width/height so it renders at the
72
+ * right size when bound via `[innerHTML]` (Angular's CSS encapsulation does not reach
73
+ * dynamically injected content). Matches the Card/Button icon helper.
74
+ */
75
+ buildIcon(name, size) {
76
+ const svg = (LUCIDE_ICONS[name] ?? '').replace('<svg ', `<svg width="${size}" height="${size}" `);
77
+ return this.sanitizer.bypassSecurityTrustHtml(svg);
78
+ }
79
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogSurfaceComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
80
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: DialogSurfaceComponent, isStandalone: true, selector: "cmp-dialog-surface", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null }, titleId: { classPropertyName: "titleId", publicName: "titleId", isSignal: true, isRequired: false, transformFunction: null }, descId: { classPropertyName: "descId", publicName: "descId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closeClick: "closeClick" }, ngImport: i0, template: "<div\r\n class=\"cmp-dialog\"\r\n [class.cmp-dialog--leading]=\"variant() === 'modal-leading-icon'\"\r\n [class.cmp-dialog--centered]=\"variant() === 'modal-centered-icon'\"\r\n [class.cmp-dialog--alert]=\"variant() === 'alert'\"\r\n [style.--cmp-dialog-width]=\"width() || null\"\r\n>\r\n @if (variant() === 'modal-leading-icon') {\r\n <header class=\"cmp-dialog__header\">\r\n <div class=\"cmp-dialog__heading\">\r\n @if (icon()) {\r\n <span class=\"cmp-dialog__lead-icon\" aria-hidden=\"true\" [innerHTML]=\"iconMarkup()\"></span>\r\n }\r\n @if (title()) {\r\n <h2 class=\"cmp-dialog__title\" [id]=\"titleId() || null\">{{ title() }}</h2>\r\n }\r\n </div>\r\n @if (dismissible()) {\r\n <button type=\"button\" class=\"cmp-dialog__close\" [attr.aria-label]=\"closeLabel()\" (click)=\"closeClick.emit()\">\r\n <span class=\"cmp-dialog__close-icon\" aria-hidden=\"true\" [innerHTML]=\"closeMarkup()\"></span>\r\n </button>\r\n }\r\n </header>\r\n } @else if (dismissible()) {\r\n <button\r\n type=\"button\"\r\n class=\"cmp-dialog__close cmp-dialog__close--floating\"\r\n [attr.aria-label]=\"closeLabel()\"\r\n (click)=\"closeClick.emit()\"\r\n >\r\n <span class=\"cmp-dialog__close-icon\" aria-hidden=\"true\" [innerHTML]=\"closeMarkup()\"></span>\r\n </button>\r\n }\r\n\r\n <div class=\"cmp-dialog__body\">\r\n @if (variant() === 'modal-centered-icon' && icon()) {\r\n <span class=\"cmp-dialog__badge\" aria-hidden=\"true\">\r\n <span class=\"cmp-dialog__badge-inner\" [innerHTML]=\"centeredIconMarkup()\"></span>\r\n </span>\r\n }\r\n @if (variant() !== 'modal-leading-icon' && (title() || description())) {\r\n <div class=\"cmp-dialog__text\">\r\n @if (title()) {\r\n <h2 class=\"cmp-dialog__title\" [id]=\"titleId() || null\">{{ title() }}</h2>\r\n }\r\n @if (description()) {\r\n <p class=\"cmp-dialog__desc\" [id]=\"descId() || null\">{{ description() }}</p>\r\n }\r\n </div>\r\n }\r\n <ng-content />\r\n </div>\r\n\r\n @if (showActions()) {\r\n <footer class=\"cmp-dialog__actions\">\r\n <ng-content select=\"[cmpDialogActions]\" />\r\n </footer>\r\n }\r\n</div>\r\n", styles: [":host{display:contents}.cmp-dialog{box-sizing:border-box;display:flex;flex-direction:column;position:relative;max-height:calc(100vh - var(--spacing-padding-sys-padding-48));background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-lg-increased);box-shadow:0 4px 16px #00000029,0 2px 6px #0000001f;overflow:hidden;animation:cmp-dialog-in .16s ease-out}.cmp-dialog--leading,.cmp-dialog--centered{width:min(var(--cmp-dialog-width, 800px),calc(100vw - var(--spacing-padding-sys-padding-32)))}.cmp-dialog--alert{width:min(var(--cmp-dialog-width, 600px),calc(100vw - var(--spacing-padding-sys-padding-32)))}@keyframes cmp-dialog-in{0%{opacity:0;transform:translateY(8px) scale(.98)}to{opacity:1;transform:none}}.cmp-dialog__header{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-padding-sys-padding-12);padding:var(--spacing-padding-sys-padding-20) var(--spacing-padding-sys-padding-32);border-bottom:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}.cmp-dialog__heading{display:flex;align-items:center;gap:var(--spacing-padding-sys-padding-12);min-width:0}.cmp-dialog__lead-icon{display:inline-flex;flex-shrink:0;color:var(--surface-surface-sys-on-surface);line-height:0}.cmp-dialog__title{margin:0;font-family:var(--title-medium-sys-font-family);font-size:var(--title-medium-sys-font-size);line-height:var(--title-medium-sys-line-height);font-weight:var(--title-medium-sys-font-weight-emphasized);letter-spacing:var(--title-medium-sys-tracking);color:var(--surface-surface-sys-on-surface);min-width:0}.cmp-dialog__desc{margin:0;font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height);letter-spacing:var(--body-medium-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.cmp-dialog__body{flex:1 1 auto;min-height:0;overflow:auto}.cmp-dialog--leading .cmp-dialog__body{padding:var(--spacing-padding-sys-padding-24) var(--spacing-padding-sys-padding-32)}.cmp-dialog--centered .cmp-dialog__body{display:flex;flex-direction:column;align-items:center;gap:var(--spacing-padding-sys-padding-24);padding:var(--spacing-padding-sys-padding-48) var(--spacing-padding-sys-padding-24);text-align:center}.cmp-dialog--alert .cmp-dialog__body{padding:var(--spacing-padding-sys-padding-24) var(--spacing-padding-sys-padding-32) 0}.cmp-dialog__text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-8)}.cmp-dialog--centered .cmp-dialog__text{align-items:center}.cmp-dialog__badge{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;padding:var(--spacing-padding-sys-padding-4);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-full);background-color:var(--surface-surface-container-sys-surface-container-lowest)}.cmp-dialog__badge-inner{display:inline-flex;align-items:center;justify-content:center;padding:var(--spacing-padding-sys-padding-20);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-full);background-image:linear-gradient(180deg,var(--surface-surface-container-sys-surface-container-high) 0%,var(--surface-surface-container-sys-surface-container-low) 100%);color:var(--surface-surface-sys-on-surface);line-height:0}.cmp-dialog__close{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;position:relative;overflow:hidden;box-sizing:border-box;width:32px;height:32px;border:none;border-radius:var(--border-radius-sys-radius-full);background-color:var(--surface-surface-container-sys-surface-container);color:var(--surface-surface-sys-on-surface-variant);cursor:pointer}.cmp-dialog__close-icon{display:inline-flex;line-height:0;position:relative;z-index:1}.cmp-dialog__close:before{content:\"\";position:absolute;inset:0;background-color:currentColor;opacity:0;transition:opacity .14s ease-out}.cmp-dialog__close:hover:before{opacity:.08}.cmp-dialog__close:active:before{opacity:.12}.cmp-dialog__close: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-dialog__close--floating{position:absolute;top:var(--spacing-padding-sys-padding-16);inset-inline-end:var(--spacing-padding-sys-padding-16);z-index:2}.cmp-dialog__actions{display:flex;align-items:center;padding:var(--spacing-padding-sys-padding-24) var(--spacing-padding-sys-padding-32);border-top:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}.cmp-dialog--leading .cmp-dialog__actions,.cmp-dialog--centered .cmp-dialog__actions{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:var(--spacing-padding-sys-padding-16)}.cmp-dialog--alert .cmp-dialog__actions{justify-content:flex-end;gap:var(--spacing-padding-sys-padding-12);padding:var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-32);border-top:none}.cmp-dialog--leading .cmp-dialog__actions:not(:has(*)),.cmp-dialog--centered .cmp-dialog__actions:not(:has(*)),.cmp-dialog--alert .cmp-dialog__actions:not(:has(*)){display:none}@media(prefers-reduced-motion:reduce){.cmp-dialog{animation:none}.cmp-dialog__close:before{transition:none}}@media(forced-colors:active){.cmp-dialog{border:1px solid CanvasText}.cmp-dialog__header,.cmp-dialog__actions{border-color:CanvasText}.cmp-dialog__close:focus-visible{outline:2px solid Highlight;outline-offset:2px}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
81
+ }
82
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogSurfaceComponent, decorators: [{
83
+ type: Component,
84
+ args: [{ selector: 'cmp-dialog-surface', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\r\n class=\"cmp-dialog\"\r\n [class.cmp-dialog--leading]=\"variant() === 'modal-leading-icon'\"\r\n [class.cmp-dialog--centered]=\"variant() === 'modal-centered-icon'\"\r\n [class.cmp-dialog--alert]=\"variant() === 'alert'\"\r\n [style.--cmp-dialog-width]=\"width() || null\"\r\n>\r\n @if (variant() === 'modal-leading-icon') {\r\n <header class=\"cmp-dialog__header\">\r\n <div class=\"cmp-dialog__heading\">\r\n @if (icon()) {\r\n <span class=\"cmp-dialog__lead-icon\" aria-hidden=\"true\" [innerHTML]=\"iconMarkup()\"></span>\r\n }\r\n @if (title()) {\r\n <h2 class=\"cmp-dialog__title\" [id]=\"titleId() || null\">{{ title() }}</h2>\r\n }\r\n </div>\r\n @if (dismissible()) {\r\n <button type=\"button\" class=\"cmp-dialog__close\" [attr.aria-label]=\"closeLabel()\" (click)=\"closeClick.emit()\">\r\n <span class=\"cmp-dialog__close-icon\" aria-hidden=\"true\" [innerHTML]=\"closeMarkup()\"></span>\r\n </button>\r\n }\r\n </header>\r\n } @else if (dismissible()) {\r\n <button\r\n type=\"button\"\r\n class=\"cmp-dialog__close cmp-dialog__close--floating\"\r\n [attr.aria-label]=\"closeLabel()\"\r\n (click)=\"closeClick.emit()\"\r\n >\r\n <span class=\"cmp-dialog__close-icon\" aria-hidden=\"true\" [innerHTML]=\"closeMarkup()\"></span>\r\n </button>\r\n }\r\n\r\n <div class=\"cmp-dialog__body\">\r\n @if (variant() === 'modal-centered-icon' && icon()) {\r\n <span class=\"cmp-dialog__badge\" aria-hidden=\"true\">\r\n <span class=\"cmp-dialog__badge-inner\" [innerHTML]=\"centeredIconMarkup()\"></span>\r\n </span>\r\n }\r\n @if (variant() !== 'modal-leading-icon' && (title() || description())) {\r\n <div class=\"cmp-dialog__text\">\r\n @if (title()) {\r\n <h2 class=\"cmp-dialog__title\" [id]=\"titleId() || null\">{{ title() }}</h2>\r\n }\r\n @if (description()) {\r\n <p class=\"cmp-dialog__desc\" [id]=\"descId() || null\">{{ description() }}</p>\r\n }\r\n </div>\r\n }\r\n <ng-content />\r\n </div>\r\n\r\n @if (showActions()) {\r\n <footer class=\"cmp-dialog__actions\">\r\n <ng-content select=\"[cmpDialogActions]\" />\r\n </footer>\r\n }\r\n</div>\r\n", styles: [":host{display:contents}.cmp-dialog{box-sizing:border-box;display:flex;flex-direction:column;position:relative;max-height:calc(100vh - var(--spacing-padding-sys-padding-48));background-color:var(--surface-surface-container-sys-surface-container-lowest);color:var(--surface-surface-sys-on-surface);font-family:var(--body-medium-sys-font-family);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-lg-increased);box-shadow:0 4px 16px #00000029,0 2px 6px #0000001f;overflow:hidden;animation:cmp-dialog-in .16s ease-out}.cmp-dialog--leading,.cmp-dialog--centered{width:min(var(--cmp-dialog-width, 800px),calc(100vw - var(--spacing-padding-sys-padding-32)))}.cmp-dialog--alert{width:min(var(--cmp-dialog-width, 600px),calc(100vw - var(--spacing-padding-sys-padding-32)))}@keyframes cmp-dialog-in{0%{opacity:0;transform:translateY(8px) scale(.98)}to{opacity:1;transform:none}}.cmp-dialog__header{display:flex;align-items:center;justify-content:space-between;gap:var(--spacing-padding-sys-padding-12);padding:var(--spacing-padding-sys-padding-20) var(--spacing-padding-sys-padding-32);border-bottom:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}.cmp-dialog__heading{display:flex;align-items:center;gap:var(--spacing-padding-sys-padding-12);min-width:0}.cmp-dialog__lead-icon{display:inline-flex;flex-shrink:0;color:var(--surface-surface-sys-on-surface);line-height:0}.cmp-dialog__title{margin:0;font-family:var(--title-medium-sys-font-family);font-size:var(--title-medium-sys-font-size);line-height:var(--title-medium-sys-line-height);font-weight:var(--title-medium-sys-font-weight-emphasized);letter-spacing:var(--title-medium-sys-tracking);color:var(--surface-surface-sys-on-surface);min-width:0}.cmp-dialog__desc{margin:0;font-family:var(--body-medium-sys-font-family);font-size:var(--body-medium-sys-font-size);line-height:var(--body-medium-sys-line-height);letter-spacing:var(--body-medium-sys-tracking);color:var(--surface-surface-sys-on-surface-variant)}.cmp-dialog__body{flex:1 1 auto;min-height:0;overflow:auto}.cmp-dialog--leading .cmp-dialog__body{padding:var(--spacing-padding-sys-padding-24) var(--spacing-padding-sys-padding-32)}.cmp-dialog--centered .cmp-dialog__body{display:flex;flex-direction:column;align-items:center;gap:var(--spacing-padding-sys-padding-24);padding:var(--spacing-padding-sys-padding-48) var(--spacing-padding-sys-padding-24);text-align:center}.cmp-dialog--alert .cmp-dialog__body{padding:var(--spacing-padding-sys-padding-24) var(--spacing-padding-sys-padding-32) 0}.cmp-dialog__text{display:flex;flex-direction:column;gap:var(--spacing-padding-sys-padding-8)}.cmp-dialog--centered .cmp-dialog__text{align-items:center}.cmp-dialog__badge{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;padding:var(--spacing-padding-sys-padding-4);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-full);background-color:var(--surface-surface-container-sys-surface-container-lowest)}.cmp-dialog__badge-inner{display:inline-flex;align-items:center;justify-content:center;padding:var(--spacing-padding-sys-padding-20);border:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline);border-radius:var(--border-radius-sys-radius-full);background-image:linear-gradient(180deg,var(--surface-surface-container-sys-surface-container-high) 0%,var(--surface-surface-container-sys-surface-container-low) 100%);color:var(--surface-surface-sys-on-surface);line-height:0}.cmp-dialog__close{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;position:relative;overflow:hidden;box-sizing:border-box;width:32px;height:32px;border:none;border-radius:var(--border-radius-sys-radius-full);background-color:var(--surface-surface-container-sys-surface-container);color:var(--surface-surface-sys-on-surface-variant);cursor:pointer}.cmp-dialog__close-icon{display:inline-flex;line-height:0;position:relative;z-index:1}.cmp-dialog__close:before{content:\"\";position:absolute;inset:0;background-color:currentColor;opacity:0;transition:opacity .14s ease-out}.cmp-dialog__close:hover:before{opacity:.08}.cmp-dialog__close:active:before{opacity:.12}.cmp-dialog__close: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-dialog__close--floating{position:absolute;top:var(--spacing-padding-sys-padding-16);inset-inline-end:var(--spacing-padding-sys-padding-16);z-index:2}.cmp-dialog__actions{display:flex;align-items:center;padding:var(--spacing-padding-sys-padding-24) var(--spacing-padding-sys-padding-32);border-top:var(--border-width-sys-stroke-thin) solid var(--outline-sys-outline)}.cmp-dialog--leading .cmp-dialog__actions,.cmp-dialog--centered .cmp-dialog__actions{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:var(--spacing-padding-sys-padding-16)}.cmp-dialog--alert .cmp-dialog__actions{justify-content:flex-end;gap:var(--spacing-padding-sys-padding-12);padding:var(--spacing-padding-sys-padding-16) var(--spacing-padding-sys-padding-32);border-top:none}.cmp-dialog--leading .cmp-dialog__actions:not(:has(*)),.cmp-dialog--centered .cmp-dialog__actions:not(:has(*)),.cmp-dialog--alert .cmp-dialog__actions:not(:has(*)){display:none}@media(prefers-reduced-motion:reduce){.cmp-dialog{animation:none}.cmp-dialog__close:before{transition:none}}@media(forced-colors:active){.cmp-dialog{border:1px solid CanvasText}.cmp-dialog__header,.cmp-dialog__actions{border-color:CanvasText}.cmp-dialog__close:focus-visible{outline:2px solid Highlight;outline-offset:2px}}\n"] }]
85
+ }], ctorParameters: () => [], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], dismissible: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissible", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], titleId: [{ type: i0.Input, args: [{ isSignal: true, alias: "titleId", required: false }] }], descId: [{ type: i0.Input, args: [{ isSignal: true, alias: "descId", required: false }] }], closeClick: [{ type: i0.Output, args: ["closeClick"] }] } });
86
+ //# sourceMappingURL=dialog-surface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog-surface.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/dialog/dialog-surface.ts","../../../../../../packages/angular/src/lib/dialog/dialog-surface.html"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,SAAS,EACT,QAAQ,EACR,MAAM,EACN,KAAK,EACL,MAAM,GACP,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAY,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAC;;AAG5F;;;;;;GAMG;AACH,IAAI,aAAa,GAAG,KAAK,CAAC;AAC1B,SAAS,iBAAiB;IACxB,IAAI,aAAa,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACrD,OAAO;IACT,CAAC;IACD,aAAa,GAAG,IAAI,CAAC;IACrB,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,KAAK,CAAC,YAAY,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;IAChD,KAAK,CAAC,WAAW;QACf,qCAAqC;YACrC,8EAA8E,CAAC;IACjF,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AACnC,CAAC;AAED;;;;;;;;;;GAUG;AAQH,MAAM,OAAO,sBAAsB;IACjC,mBAAmB;IACV,OAAO,GAAG,KAAK,CAAgB,oBAAoB,8EAAC,CAAC;IAC9D,4EAA4E;IACnE,KAAK,GAAG,KAAK,CAAS,EAAE,4EAAC,CAAC;IACnC,oBAAoB;IACX,KAAK,GAAG,KAAK,CAAS,EAAE,4EAAC,CAAC;IACnC,sEAAsE;IAC7D,WAAW,GAAG,KAAK,CAAS,EAAE,kFAAC,CAAC;IACzC,qEAAqE;IAC5D,IAAI,GAAG,KAAK,CAAS,EAAE,2EAAC,CAAC;IAClC,yCAAyC;IAChC,WAAW,GAAG,KAAK,CAAU,IAAI,kFAAC,CAAC;IAC5C,6CAA6C;IACpC,UAAU,GAAG,KAAK,CAAS,cAAc,iFAAC,CAAC;IACpD,wDAAwD;IAC/C,WAAW,GAAG,KAAK,CAAU,IAAI,kFAAC,CAAC;IAC5C,yEAAyE;IAChE,OAAO,GAAG,KAAK,CAAS,EAAE,8EAAC,CAAC;IACrC,gFAAgF;IACvE,MAAM,GAAG,KAAK,CAAS,EAAE,6EAAC,CAAC;IAEpC,kDAAkD;IACzC,UAAU,GAAG,MAAM,EAAQ,CAAC;IAEpB,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IAElD;QACE,+FAA+F;QAC/F,iBAAiB,EAAE,CAAC;IACtB,CAAC;IAED,4CAA4C;IACzB,UAAU,GAAG,QAAQ,CAAW,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,iFAAC,CAAC;IAC1F,0CAA0C;IACvB,kBAAkB,GAAG,QAAQ,CAAW,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,yFAAC,CAAC;IAClG,uCAAuC;IACpB,WAAW,GAAG,QAAQ,CAAW,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,CAAC,kFAAC,CAAC;IAEnF;;;;OAIG;IACK,SAAS,CAAC,IAAY,EAAE,IAAY;QAC1C,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,eAAe,IAAI,aAAa,IAAI,IAAI,CAAC,CAAC;QAClG,OAAO,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAC;IACrD,CAAC;wGA/CU,sBAAsB;4FAAtB,sBAAsB,k4CCnDnC,6wEA2DA;;4FDRa,sBAAsB;kBAPlC,SAAS;+BACE,oBAAoB,cAClB,IAAI,mBACC,uBAAuB,CAAC,MAAM","sourcesContent":["import {\r\n ChangeDetectionStrategy,\r\n Component,\r\n computed,\r\n inject,\r\n input,\r\n output,\r\n} from '@angular/core';\r\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\r\nimport { LUCIDE_ICONS } from '@compsych-ui-components/shared/assets/lucide-icons.generated';\r\nimport { DialogVariant } from './dialog-types';\r\n\r\n/**\r\n * The backdrop scrim colors the CDK overlay backdrop, which lives outside this component's\r\n * view encapsulation — so it can't be a scoped stylesheet. The dialog owns it itself (no\r\n * separate export or consumer import): the first surface to render injects one `<style>`\r\n * into `<head>`. Idempotent, browser-only. Figma \"Scrim/Overlay\" (61041:7560): on-surface 10%.\r\n * Openers set the backdrop's class to `cmp-dialog-scrim`.\r\n */\r\nlet scrimInjected = false;\r\nfunction injectScrimStyles(): void {\r\n if (scrimInjected || typeof document === 'undefined') {\r\n return;\r\n }\r\n scrimInjected = true;\r\n const style = document.createElement('style');\r\n style.setAttribute('data-cmp-dialog-scrim', '');\r\n style.textContent =\r\n '.cmp-dialog-scrim{background-color:' +\r\n 'color-mix(in srgb, var(--surface-surface-sys-on-surface) 10%, transparent);}';\r\n document.head.appendChild(style);\r\n}\r\n\r\n/**\r\n * Internal presentational chrome for the dialog: the surface box, header (leading icon +\r\n * title + close, or a floating close), centered icon badge + title/description, the body\r\n * slot, and the actions footer. Rendered by BOTH the declarative `compsych-dialog` and the\r\n * imperative `DialogService` container, so the chrome has a single source of truth.\r\n *\r\n * Not part of the public API. Overlay/a11y state (role, aria-modal, focus, open) is owned\r\n * by the host (the CDK dialog container); `titleId`/`descId` are supplied by the host so its\r\n * `aria-labelledby`/`aria-describedby` can reference the rendered title/description elements.\r\n * The host renders `display: contents`, so `.cmp-dialog` is the layout box.\r\n */\r\n@Component({\r\n selector: 'cmp-dialog-surface',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n templateUrl: './dialog-surface.html',\r\n styleUrl: './dialog-surface.css',\r\n})\r\nexport class DialogSurfaceComponent {\r\n /** Layout type. */\r\n readonly variant = input<DialogVariant>('modal-leading-icon');\r\n /** Optional CSS-length width override (falls back to the variant width). */\r\n readonly width = input<string>('');\r\n /** Heading text. */\r\n readonly title = input<string>('');\r\n /** Supporting text (rendered for `centered` and `alert` variants). */\r\n readonly description = input<string>('');\r\n /** Lucide icon name (leading header icon / centered badge glyph). */\r\n readonly icon = input<string>('');\r\n /** Whether the close button is shown. */\r\n readonly dismissible = input<boolean>(true);\r\n /** Accessible label for the close button. */\r\n readonly closeLabel = input<string>('Close dialog');\r\n /** Whether the projected actions footer is rendered. */\r\n readonly showActions = input<boolean>(true);\r\n /** Id for the title element (host supplies it for `aria-labelledby`). */\r\n readonly titleId = input<string>('');\r\n /** Id for the description element (host supplies it for `aria-describedby`). */\r\n readonly descId = input<string>('');\r\n\r\n /** Emitted when the close button is activated. */\r\n readonly closeClick = output<void>();\r\n\r\n private readonly sanitizer = inject(DomSanitizer);\r\n\r\n constructor() {\r\n // Self-contained scrim: inject the backdrop style on first render (no separate export/import).\r\n injectScrimStyles();\r\n }\r\n\r\n /** Header (leading) icon markup at 24px. */\r\n protected readonly iconMarkup = computed<SafeHtml>(() => this.buildIcon(this.icon(), 24));\r\n /** Centered badge icon markup at 32px. */\r\n protected readonly centeredIconMarkup = computed<SafeHtml>(() => this.buildIcon(this.icon(), 32));\r\n /** Close (`x`) icon markup at 20px. */\r\n protected readonly closeMarkup = computed<SafeHtml>(() => this.buildIcon('x', 20));\r\n\r\n /**\r\n * Looks up a Lucide icon by name and injects an explicit width/height so it renders at the\r\n * right size when bound via `[innerHTML]` (Angular's CSS encapsulation does not reach\r\n * dynamically injected content). Matches the Card/Button icon helper.\r\n */\r\n private buildIcon(name: string, size: number): SafeHtml {\r\n const svg = (LUCIDE_ICONS[name] ?? '').replace('<svg ', `<svg width=\"${size}\" height=\"${size}\" `);\r\n return this.sanitizer.bypassSecurityTrustHtml(svg);\r\n }\r\n}\r\n","<div\r\n class=\"cmp-dialog\"\r\n [class.cmp-dialog--leading]=\"variant() === 'modal-leading-icon'\"\r\n [class.cmp-dialog--centered]=\"variant() === 'modal-centered-icon'\"\r\n [class.cmp-dialog--alert]=\"variant() === 'alert'\"\r\n [style.--cmp-dialog-width]=\"width() || null\"\r\n>\r\n @if (variant() === 'modal-leading-icon') {\r\n <header class=\"cmp-dialog__header\">\r\n <div class=\"cmp-dialog__heading\">\r\n @if (icon()) {\r\n <span class=\"cmp-dialog__lead-icon\" aria-hidden=\"true\" [innerHTML]=\"iconMarkup()\"></span>\r\n }\r\n @if (title()) {\r\n <h2 class=\"cmp-dialog__title\" [id]=\"titleId() || null\">{{ title() }}</h2>\r\n }\r\n </div>\r\n @if (dismissible()) {\r\n <button type=\"button\" class=\"cmp-dialog__close\" [attr.aria-label]=\"closeLabel()\" (click)=\"closeClick.emit()\">\r\n <span class=\"cmp-dialog__close-icon\" aria-hidden=\"true\" [innerHTML]=\"closeMarkup()\"></span>\r\n </button>\r\n }\r\n </header>\r\n } @else if (dismissible()) {\r\n <button\r\n type=\"button\"\r\n class=\"cmp-dialog__close cmp-dialog__close--floating\"\r\n [attr.aria-label]=\"closeLabel()\"\r\n (click)=\"closeClick.emit()\"\r\n >\r\n <span class=\"cmp-dialog__close-icon\" aria-hidden=\"true\" [innerHTML]=\"closeMarkup()\"></span>\r\n </button>\r\n }\r\n\r\n <div class=\"cmp-dialog__body\">\r\n @if (variant() === 'modal-centered-icon' && icon()) {\r\n <span class=\"cmp-dialog__badge\" aria-hidden=\"true\">\r\n <span class=\"cmp-dialog__badge-inner\" [innerHTML]=\"centeredIconMarkup()\"></span>\r\n </span>\r\n }\r\n @if (variant() !== 'modal-leading-icon' && (title() || description())) {\r\n <div class=\"cmp-dialog__text\">\r\n @if (title()) {\r\n <h2 class=\"cmp-dialog__title\" [id]=\"titleId() || null\">{{ title() }}</h2>\r\n }\r\n @if (description()) {\r\n <p class=\"cmp-dialog__desc\" [id]=\"descId() || null\">{{ description() }}</p>\r\n }\r\n </div>\r\n }\r\n <ng-content />\r\n </div>\r\n\r\n @if (showActions()) {\r\n <footer class=\"cmp-dialog__actions\">\r\n <ng-content select=\"[cmpDialogActions]\" />\r\n </footer>\r\n }\r\n</div>\r\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dialog-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog-types.js","sourceRoot":"","sources":["../../../../../../packages/angular/src/lib/dialog/dialog-types.ts"],"names":[],"mappings":"","sourcesContent":["/**\r\n * Dialog layout type — mirrors the Figma \"Dialog\" organism `Type` property\r\n * (file VFBn7KCDy3FSIlvhNo3ylq, 873:9194).\r\n *\r\n * - `'modal-leading-icon'` — header row with an optional leading icon + title\r\n * and a close button, a projected body, and a\r\n * full-width split action footer. Widest surface.\r\n * - `'modal-centered-icon'` — a centered icon badge above a centered title +\r\n * description, with a full-width split action footer.\r\n * - `'alert'` — compact: left-aligned title + description with the\r\n * actions right-aligned. Narrowest surface.\r\n */\r\nexport type DialogVariant = 'modal-leading-icon' | 'modal-centered-icon' | 'alert';\r\n"]}
@@ -0,0 +1,177 @@
1
+ import { ChangeDetectionStrategy, Component, ViewChild, ViewContainerRef, effect, inject, input, isDevMode, model, output, } from '@angular/core';
2
+ import { Dialog } from '@angular/cdk/dialog';
3
+ import { Directionality } from '@angular/cdk/bidi';
4
+ import { Overlay } from '@angular/cdk/overlay';
5
+ import { nextDialogIds } from './dialog-id';
6
+ import { DialogSurfaceComponent } from './dialog-surface';
7
+ import * as i0 from "@angular/core";
8
+ /**
9
+ * A themeable modal dialog built on `@angular/cdk/dialog` (centered, scrimmed, focus-trapped).
10
+ *
11
+ * Figma: Core Components — "Dialog" organism (file VFBn7KCDy3FSIlvhNo3ylq, 873:9194).
12
+ *
13
+ * One component, switched by `variant`:
14
+ * - `modal-leading-icon` — leading icon + title header, projected body, split footer.
15
+ * - `modal-centered-icon` — centered icon badge, centered title + description, split footer.
16
+ * - `alert` — compact title + description, right-aligned footer.
17
+ *
18
+ * Open state is a two-way `[(open)]` model wrapped around the CDK `Dialog` service. The
19
+ * CDK dialog container provides `role="dialog"`, `aria-modal`, the focus trap, initial
20
+ * focus (`autoFocus: 'dialog'`) and focus restoration. The dialog closes on Escape /
21
+ * backdrop click (when `dismissible`); those are handled here so `closed` can report a
22
+ * reason. Title/description come from inputs; project the body as default content and each
23
+ * action button with the `cmpDialogActions` attribute (the footer lays them out — split
24
+ * full-width for modals, right-aligned for alerts).
25
+ *
26
+ * <compsych-dialog [(open)]="showDialog" variant="modal-leading-icon"
27
+ * icon="globe" title="Update your details">
28
+ * <p>Body content…</p>
29
+ * <compsych-button cmpDialogActions variant="outlined" fullWidth label="Cancel" (click)="showDialog = false" />
30
+ * <compsych-button cmpDialogActions variant="filled" fullWidth label="Save" (click)="save()" />
31
+ * </compsych-dialog>
32
+ *
33
+ * Consumers must load `@angular/cdk/overlay-prebuilt.css` (overlay positioning) and the
34
+ * `@compsych-ui-components/shared` design tokens globally. The scrim color is injected by
35
+ * the dialog itself — no extra stylesheet import is needed.
36
+ */
37
+ export class DialogComponent {
38
+ /** Two-way open state. Set to `true` to show the dialog, `false` to close it. */
39
+ open = model(false, ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
40
+ /** Layout type. Defaults to `modal-leading-icon`. */
41
+ variant = input('modal-leading-icon', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
42
+ /**
43
+ * Optional width override — any CSS length (e.g. `'1000px'`). Defaults to the variant
44
+ * width (800px modal / 600px alert). Always clamped to the viewport on small screens.
45
+ */
46
+ width = input('', ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
47
+ /** Heading text. Also drives the accessible name via `aria-labelledby`. */
48
+ title = input('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
49
+ /** Supporting text under the title (rendered for `centered` and `alert` variants). */
50
+ description = input('', ...(ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []));
51
+ /** Lucide icon name — the leading header icon (`leading`) or centered badge glyph (`centered`). */
52
+ icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
53
+ /**
54
+ * When true (default) the dialog shows a close button and closes on Escape / backdrop click.
55
+ * When false, the dialog is a forced choice — provide a focusable action (e.g. a
56
+ * `[cmpDialogActions]` button) so keyboard users can dismiss it (WCAG 2.1.2, no keyboard trap).
57
+ */
58
+ dismissible = input(true, ...(ngDevMode ? [{ debugName: "dismissible" }] : /* istanbul ignore next */ []));
59
+ /** Accessible label for the close button. */
60
+ closeLabel = input('Close dialog', ...(ngDevMode ? [{ debugName: "closeLabel" }] : /* istanbul ignore next */ []));
61
+ /** Accessible name used when no `title` is provided (e.g. a fully custom body). */
62
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
63
+ /** Renders the action footer. Set false to hide it when there are no actions. */
64
+ showActions = input(true, ...(ngDevMode ? [{ debugName: "showActions" }] : /* istanbul ignore next */ []));
65
+ /** Emits after the dialog has been opened and shown. */
66
+ opened = output();
67
+ /** Emits after the dialog has closed, with the reason it closed. */
68
+ closed = output();
69
+ /** The dialog surface template, projected into the CDK dialog container when open. */
70
+ panelTemplate;
71
+ dialog = inject(Dialog);
72
+ overlay = inject(Overlay);
73
+ directionality = inject(Directionality);
74
+ viewContainerRef = inject(ViewContainerRef);
75
+ dialogRef;
76
+ closeReason = 'programmatic';
77
+ _ids = nextDialogIds();
78
+ titleId = this._ids.titleId;
79
+ descId = this._ids.descId;
80
+ constructor() {
81
+ // Reflect the open model onto the CDK dialog lifecycle.
82
+ effect(() => {
83
+ if (this.open() && !this.dialogRef) {
84
+ this.attach();
85
+ }
86
+ else if (!this.open() && this.dialogRef) {
87
+ this.detach();
88
+ }
89
+ });
90
+ }
91
+ ngOnDestroy() {
92
+ this.dialogRef?.close();
93
+ }
94
+ /** Close request from the close button. */
95
+ requestClose() {
96
+ this.closeWith('close-button');
97
+ }
98
+ closeWith(reason) {
99
+ if (!this.open()) {
100
+ return;
101
+ }
102
+ this.closeReason = reason;
103
+ this.open.set(false);
104
+ }
105
+ attach() {
106
+ this.closeReason = 'programmatic';
107
+ // Dev-only guardrail: a dialog with no title and no ariaLabel has no accessible
108
+ // name (WCAG 2.2 §4.1.2). Stripped from production builds by isDevMode().
109
+ if (isDevMode() && !this.title() && !this.ariaLabel()) {
110
+ console.warn('[compsych-dialog] Opened with no accessible name — set `title` or `ariaLabel` ' +
111
+ 'so assistive technology can identify the dialog (WCAG 2.2 §4.1.2).');
112
+ }
113
+ const ref = this.dialog.open(this.panelTemplate, {
114
+ viewContainerRef: this.viewContainerRef,
115
+ hasBackdrop: true,
116
+ // Scrim color is injected by the dialog surface itself; positioning/fade come from
117
+ // @angular/cdk/overlay-prebuilt.css (which consumers load).
118
+ backdropClass: 'cmp-dialog-scrim',
119
+ panelClass: 'cmp-dialog-pane',
120
+ positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),
121
+ scrollStrategy: this.overlay.scrollStrategies.block(),
122
+ // Forward the local reading direction so a body-portaled overlay mirrors correctly
123
+ // even when only a subtree (not the document) is RTL.
124
+ direction: this.directionality.value,
125
+ // The `alert` variant is a decision/confirmation dialog → assertive alertdialog role.
126
+ role: this.variant() === 'alert' ? 'alertdialog' : 'dialog',
127
+ // We handle Escape / backdrop ourselves so `closed` can report a reason.
128
+ disableClose: true,
129
+ // Focus the dialog container on open (screen reader announces it; first Tab then
130
+ // lands on the close button with its visible ring) and restore focus on close.
131
+ autoFocus: 'dialog',
132
+ restoreFocus: true,
133
+ ariaModal: true,
134
+ ariaLabelledBy: this.title() ? this.titleId : null,
135
+ ariaDescribedBy: this.variant() !== 'modal-leading-icon' && this.description() ? this.descId : null,
136
+ // Accessible name for the no-title custom-body case (CDK nulls aria-labelledby when set).
137
+ ariaLabel: !this.title() ? this.ariaLabel() || null : null,
138
+ });
139
+ this.dialogRef = ref;
140
+ ref.backdropClick.subscribe(() => {
141
+ if (this.dismissible()) {
142
+ this.closeWith('backdrop');
143
+ }
144
+ });
145
+ ref.keydownEvents.subscribe((event) => {
146
+ if (event.key === 'Escape' && this.dismissible()) {
147
+ event.preventDefault();
148
+ this.closeWith('escape');
149
+ }
150
+ });
151
+ ref.closed.subscribe(() => {
152
+ this.dialogRef = undefined;
153
+ const reason = this.closeReason;
154
+ this.closeReason = 'programmatic';
155
+ // Keep the model in sync if the dialog was torn down by CDK rather than the model.
156
+ if (this.open()) {
157
+ this.open.set(false);
158
+ }
159
+ this.closed.emit(reason);
160
+ });
161
+ this.opened.emit();
162
+ }
163
+ detach() {
164
+ // Triggers `ref.closed`, which clears state, restores focus, and emits `closed`.
165
+ this.dialogRef?.close();
166
+ }
167
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
168
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.14", type: DialogComponent, isStandalone: true, selector: "compsych-dialog", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, description: { classPropertyName: "description", publicName: "description", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange", opened: "opened", closed: "closed" }, host: { classAttribute: "cmp-dialog-host" }, viewQueries: [{ propertyName: "panelTemplate", first: true, predicate: ["panel"], descendants: true, static: true }], ngImport: i0, template: "<ng-template #panel>\r\n <cmp-dialog-surface\r\n [variant]=\"variant()\"\r\n [width]=\"width()\"\r\n [title]=\"title()\"\r\n [description]=\"description()\"\r\n [icon]=\"icon()\"\r\n [dismissible]=\"dismissible()\"\r\n [closeLabel]=\"closeLabel()\"\r\n [showActions]=\"showActions()\"\r\n [titleId]=\"titleId\"\r\n [descId]=\"descId\"\r\n (closeClick)=\"requestClose()\"\r\n >\r\n <ng-content />\r\n <ng-content select=\"[cmpDialogActions]\" ngProjectAs=\"[cmpDialogActions]\" />\r\n </cmp-dialog-surface>\r\n</ng-template>\r\n", styles: [":host{display:none}\n"], dependencies: [{ kind: "component", type: DialogSurfaceComponent, selector: "cmp-dialog-surface", inputs: ["variant", "width", "title", "description", "icon", "dismissible", "closeLabel", "showActions", "titleId", "descId"], outputs: ["closeClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
169
+ }
170
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: DialogComponent, decorators: [{
171
+ type: Component,
172
+ args: [{ selector: 'compsych-dialog', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [DialogSurfaceComponent], host: { class: 'cmp-dialog-host' }, template: "<ng-template #panel>\r\n <cmp-dialog-surface\r\n [variant]=\"variant()\"\r\n [width]=\"width()\"\r\n [title]=\"title()\"\r\n [description]=\"description()\"\r\n [icon]=\"icon()\"\r\n [dismissible]=\"dismissible()\"\r\n [closeLabel]=\"closeLabel()\"\r\n [showActions]=\"showActions()\"\r\n [titleId]=\"titleId\"\r\n [descId]=\"descId\"\r\n (closeClick)=\"requestClose()\"\r\n >\r\n <ng-content />\r\n <ng-content select=\"[cmpDialogActions]\" ngProjectAs=\"[cmpDialogActions]\" />\r\n </cmp-dialog-surface>\r\n</ng-template>\r\n", styles: [":host{display:none}\n"] }]
173
+ }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], dismissible: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissible", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], opened: [{ type: i0.Output, args: ["opened"] }], closed: [{ type: i0.Output, args: ["closed"] }], panelTemplate: [{
174
+ type: ViewChild,
175
+ args: ['panel', { static: true }]
176
+ }] } });
177
+ //# sourceMappingURL=dialog.js.map