UNPKG

carbon-components-angular

Version:
1,039 lines (1,020 loc) 44.1 kB
import * as i0 from '@angular/core'; import { EventEmitter, Directive, Output, Input, inject, EnvironmentInjector, Injector, Injectable, Component, ViewChild, Inject, HostListener, HostBinding, Optional, NgModule } from '@angular/core'; import * as i1$1 from '@angular/common'; import { DOCUMENT, CommonModule } from '@angular/common'; import * as i3 from 'carbon-components-angular/forms'; import { ButtonModule } from 'carbon-components-angular/forms'; import { cycleTabs, getFocusElementList } from 'carbon-components-angular/common'; import { tap, delay } from 'rxjs/operators'; import * as i1 from 'carbon-components-angular/placeholder'; import { PlaceholderModule } from 'carbon-components-angular/placeholder'; import * as i1$2 from 'carbon-components-angular/i18n'; import { I18nModule } from 'carbon-components-angular/i18n'; import * as i4 from 'carbon-components-angular/icon'; import { IconModule } from 'carbon-components-angular/icon'; import { ExperimentalModule } from 'carbon-components-angular/experimental'; /** * Extend `BaseModal` in your custom modal implementations to ensure consistent close behavior. * * `ModalService` depends on the `close` event to correctly clean up the component. */ class BaseModal { constructor() { /** * Base event emitter to propagate close events */ this.close = new EventEmitter(); /** * Controls the open state of the modal */ this.open = false; } /** * Default method to handle closing the modal */ closeModal() { this.close.emit(); } } BaseModal.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseModal, deps: [], target: i0.ɵɵFactoryTarget.Directive }); BaseModal.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.3.0", type: BaseModal, selector: "[cdsBaseModal], [ibmBaseModal]", inputs: { open: "open" }, outputs: { close: "close" }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseModal, decorators: [{ type: Directive, args: [{ selector: "[cdsBaseModal], [ibmBaseModal]" }] }], propDecorators: { close: [{ type: Output }], open: [{ type: Input }] } }); /** * Modal service handles instantiating and destroying modal instances. * Uses PlaceholderService to track open instances, and for it's placeholder view reference. */ class BaseModalService { /** * Creates an instance of `ModalService`. */ constructor(placeholderService) { this.placeholderService = placeholderService; /** * Current module/component injection enviornment * Allows modules to use providers from calling component * * Root Module/ * └── Lazy loaded Feature Module/ * ├── Provides Service & imports modules * ├── Modal component (component that extends base component) * └── Modal component launcher (dynamically creates modal component) * * Passing EnvironmentInjector in `createComponent` will look for provider declaration in feature * module instead of root module. This is required to pass correct context in a lazy-loaded applications. * Services injected in root, will also be available as feature module enviornment will also hierarchically inherit * the root services. */ this.environment = inject(EnvironmentInjector); } /** * Creates and renders the modal component that is passed in. * `inputs` is an optional parameter of `data` that can be passed to the `Modal` component. */ create(data) { let defaults = { inputs: {} }; data = Object.assign({}, defaults, data); const inputProviders = Object.keys(data.inputs).map(inputName => ({ provide: inputName, useValue: data.inputs[inputName] })); const injector = Injector.create({ providers: inputProviders }); const component = this.placeholderService.createComponent(data.component, injector, undefined, this.environment); let focusedElement = document.activeElement; setTimeout(() => { component.instance.open = true; }); component["previouslyFocusedElement"] = focusedElement; // used to return focus to previously focused element component.instance.close.pipe( // trigger the close animation tap(() => { component.instance.open = false; }), // delay closing by an arbitrary amount to allow the animation to finish delay(240)).subscribe(() => { this.placeholderService.destroyComponent(component); // filter out our component BaseModalService.modalList = BaseModalService.modalList.filter(c => c !== component); }); component.onDestroy(() => { focusedElement.focus(); }); BaseModalService.modalList.push(component); return component; } /** * Destroys the modal on the supplied index. * When called without parameters it destroys the most recently created/top most modal. */ destroy(index = -1) { // return if nothing to destroy because it's already destroyed if (index >= BaseModalService.modalList.length || BaseModalService.modalList.length === 0) { return; } // on negative index destroy the last on the list (top modal) if (index < 0) { index = BaseModalService.modalList.length - 1; } // Let animation finish before component is removed setTimeout(() => { if (BaseModalService.modalList[index]) { this.placeholderService.destroyComponent(BaseModalService.modalList[index]); BaseModalService.modalList.splice(index, 1); } }, 240); } } // track all our open modals BaseModalService.modalList = []; BaseModalService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseModalService, deps: [{ token: i1.PlaceholderService }], target: i0.ɵɵFactoryTarget.Injectable }); BaseModalService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseModalService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseModalService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i1.PlaceholderService }]; } }); /** * Component for the overlay object that acts as a backdrop to the `Modal` component. * * The main purpose for this component is to be able to handle click events that fall outside * the bounds of the `Modal` component. */ class Overlay { constructor() { /** * Classification of the modal. */ this.theme = "default"; this.open = false; /** * To emit the event where the user selects the overlay behind the `Modal`. */ this.overlaySelect = new EventEmitter(); } /** * Handles the user clicking on the `Overlay` which resides outside the `Modal` object. */ overlayClick(event) { if (event.target !== this.overlay.nativeElement) { return; } event.stopPropagation(); this.overlaySelect.emit(event); } } Overlay.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Overlay, deps: [], target: i0.ɵɵFactoryTarget.Component }); Overlay.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: Overlay, selector: "cds-overlay, ibm-overlay", inputs: { theme: "theme", open: "open" }, outputs: { overlaySelect: "overlaySelect" }, viewQueries: [{ propertyName: "overlay", first: true, predicate: ["overlay"], descendants: true, static: true }], ngImport: i0, template: ` <section class="cds--modal cds--modal-tall" [ngClass]="{ 'cds--modal--danger': theme === 'danger', 'is-visible': open }" (click)="overlayClick($event)" #overlay> <ng-content></ng-content> </section> `, isInline: true, dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Overlay, decorators: [{ type: Component, args: [{ selector: "cds-overlay, ibm-overlay", template: ` <section class="cds--modal cds--modal-tall" [ngClass]="{ 'cds--modal--danger': theme === 'danger', 'is-visible': open }" (click)="overlayClick($event)" #overlay> <ng-content></ng-content> </section> ` }] }], propDecorators: { theme: [{ type: Input }], open: [{ type: Input }], overlaySelect: [{ type: Output }], overlay: [{ type: ViewChild, args: ["overlay", { static: true }] }] } }); /** * Component to create modals for presenting content. * * [See demo](../../?path=/story/components-modal--basic) * * Using a modal in your application requires `cds-placeholder` which would generally be * placed near the end of your app component template (app.component.ts or app.component.html) as: * ```html <cds-placeholder></cds-placeholder> ``` * * A more complete example for `Modal` is given as follows: * * Example modal definition: * ```typescript @Component({ selector: "app-sample-modal", template: ` <cds-modal size="xl" (overlaySelected)="closeModal()"> <cds-modal-header (closeSelect)="closeModal()">Header text</cds-modal-header> <section class="modal-body"> <h1>Sample modal works.</h1> <button class="btn--icon-link" nPopover="Hello there" title="Popover title" placement="right" appendInline="true"> <svg cdsIcon="info" size="sm"></svg> </button> {{modalText}} </section> <cds-modal-footer><button cdsButton="primary" (click)="closeModal()">Close</button></cds-modal-footer> </cds-modal>`, styleUrls: ["./sample-modal.component.scss"] }) export class SampleModal extends BaseModal { modalText: string; constructor(protected injector: Injector) { super(); this.modalText = this.injector.get("modalText"); } } ``` * * Example of opening the modal: * ```typescript @Component({ selector: "app-modal-demo", template: ` <button cdsButton="primary" (click)="openModal('drill')">Drill-down modal</button> <cds-placeholder></cds-placeholder>` }) export class ModalDemo { openModal() { this.modalService.create({component: SampleModal, inputs: {modalText: "Hello universe."}}); } } ``` */ class Modal { /** * Creates an instance of `Modal`. */ constructor(modalService, document, renderer) { this.modalService = modalService; this.document = document; this.renderer = renderer; /** * Size of the modal to display. */ this.size = "md"; /** * Classification of the modal. */ this.theme = "default"; /** * Label for the modal. */ this.ariaLabel = "default"; /** * Controls the visibility of the modal when used directly in a template */ this.open = false; /** * Specify whether the modal contains scrolling content. This property overrides the automatic * detection of the existence of scrolling content. Set this property to `true` to force * overflow indicator to show up or to `false` to force overflow indicator to disappear. * It is set to `null` by default which indicates not to override automatic detection. */ this.hasScrollingContent = null; /** * Emits event when click occurs within `n-overlay` element. This is to track click events occurring outside bounds of the `Modal` object. */ this.overlaySelected = new EventEmitter(); /** * To emit the closing event of the modal window. */ this.close = new EventEmitter(); /** * An element should have 'modal-primary-focus' as an attribute to receive initial focus within the `Modal` component. */ this.selectorPrimaryFocus = "[modal-primary-focus]"; } ngOnChanges({ open, hasScrollingContent }) { if (open) { if (open.currentValue) { // `100` is just enough time to allow the modal // to become visible, so that we can set focus setTimeout(() => this.focusInitialElement(), 100); // Prevent scrolling on open this.renderer.addClass(this.document.body, "cds--body--with-modal-open"); } else if (!open.currentValue) { // Enable scrolling on close this.renderer.removeClass(this.document.body, "cds--body--with-modal-open"); } else if (this.trigger) { this.trigger.focus(); } } if (hasScrollingContent) { this.updateScrollbar(); } } /** * Set document focus to be on the modal component after it is initialized. */ ngAfterViewInit() { this.focusInitialElement(); this.updateScrollbar(); } /** * Handle keyboard events to close modal and tab through the content within the modal. */ handleKeyboardEvent(event) { switch (event.key) { case "Escape": { event.stopImmediatePropagation(); // prevents events being fired for multiple modals if more than 2 open // Manually close modal this.open = false; this.close.emit(); this.modalService.destroy(); // destroy top (latest) modal break; } case "Tab": { cycleTabs(event, this.modal.nativeElement); break; } } } /** * This detects whether or not the modal contains scrolling content. * * To force trigger a detection (ie. on window resize), change or reset the value of the modal content. * * Use the `hasScrollingContent` input to manually override the overflow indicator. */ get shouldShowScrollbar() { const modalContent = this.modal ? this.modal.nativeElement.querySelector(".cds--modal-content") : null; if (modalContent) { // get rounded value from height to match integer returned from scrollHeight const modalContentHeight = Math.ceil(modalContent.getBoundingClientRect().height); const modalContentScrollHeight = modalContent.scrollHeight; return modalContentScrollHeight > modalContentHeight; } else { return false; } } // Remove class preventing scrolling ngOnDestroy() { this.renderer.removeClass(this.document.body, "cds--body--with-modal-open"); } focusInitialElement() { const primaryFocusElement = this.modal.nativeElement.querySelector(this.selectorPrimaryFocus); if (primaryFocusElement && primaryFocusElement.focus) { setTimeout(() => primaryFocusElement.focus()); } else if (getFocusElementList(this.modal.nativeElement).length > 0) { setTimeout(() => getFocusElementList(this.modal.nativeElement)[0].focus()); } else { setTimeout(() => this.modal.nativeElement.focus()); } } updateScrollbar() { const modalContent = this.modal ? this.modal.nativeElement.querySelector(".cds--modal-content") : null; const showScrollbar = this.hasScrollingContent !== null ? this.hasScrollingContent : this.shouldShowScrollbar; if (modalContent) { if (showScrollbar) { this.renderer.addClass(modalContent, "cds--modal-scroll-content"); } else { this.renderer.removeClass(modalContent, "cds--modal-scroll-content"); } } } } Modal.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Modal, deps: [{ token: BaseModalService }, { token: DOCUMENT }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component }); Modal.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: Modal, selector: "cds-modal, ibm-modal", inputs: { size: "size", theme: "theme", ariaLabel: "ariaLabel", open: "open", trigger: "trigger", hasScrollingContent: "hasScrollingContent" }, outputs: { overlaySelected: "overlaySelected", close: "close" }, host: { listeners: { "keydown": "handleKeyboardEvent($event)" } }, viewQueries: [{ propertyName: "modal", first: true, predicate: ["modal"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: ` <cds-overlay [theme]="theme" [open]="open" (overlaySelect)="overlaySelected.emit()"> <div class="cds--modal-container" [ngClass]="{ 'cds--modal-container--xs': size === 'xs', 'cds--modal-container--sm': size === 'sm', 'cds--modal-container--md': size === 'md', 'cds--modal-container--lg': size === 'lg' }" role="dialog" aria-modal="true" style="z-index:1;" [attr.aria-label]="ariaLabel" #modal> <ng-content></ng-content> </div> </cds-overlay> `, isInline: true, dependencies: [{ kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: Overlay, selector: "cds-overlay, ibm-overlay", inputs: ["theme", "open"], outputs: ["overlaySelect"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Modal, decorators: [{ type: Component, args: [{ selector: "cds-modal, ibm-modal", template: ` <cds-overlay [theme]="theme" [open]="open" (overlaySelect)="overlaySelected.emit()"> <div class="cds--modal-container" [ngClass]="{ 'cds--modal-container--xs': size === 'xs', 'cds--modal-container--sm': size === 'sm', 'cds--modal-container--md': size === 'md', 'cds--modal-container--lg': size === 'lg' }" role="dialog" aria-modal="true" style="z-index:1;" [attr.aria-label]="ariaLabel" #modal> <ng-content></ng-content> </div> </cds-overlay> ` }] }], ctorParameters: function () { return [{ type: BaseModalService }, { type: Document, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: i0.Renderer2 }]; }, propDecorators: { size: [{ type: Input }], theme: [{ type: Input }], ariaLabel: [{ type: Input }], open: [{ type: Input }], trigger: [{ type: Input }], hasScrollingContent: [{ type: Input }], overlaySelected: [{ type: Output }], close: [{ type: Output }], modal: [{ type: ViewChild, args: ["modal", { static: true }] }], handleKeyboardEvent: [{ type: HostListener, args: ["keydown", ["$event"]] }] } }); /** * ***Inputs*** * ```html * <cds-modal-header>Header text</cds-modal-header> * ``` * * ***Outputs*** * ```html * <cds-modal-header (closeSelect)="closeModal()">Header text</cds-modal-header> * ``` */ class ModalHeader { constructor(i18n) { this.i18n = i18n; /** * @deprecated since v5 * Sets the style on the modal heading based on its category. */ this.theme = "default"; /** * Accessible label for the header close button. * Defaults to the `MODAL.CLOSE` value from the i18n service. */ this.closeLabel = this.i18n.get().MODAL.CLOSE; /** * Set to `false` to hide the close button. */ this.showCloseButton = true; /** * To emit the event of clicking on the close icon within the modal. */ this.closeSelect = new EventEmitter(); this.buttonNgClass = { "cds--modal-close": true }; this.buttonAttributes = { "aria-label": this.i18n.get().MODAL.CLOSE }; } /** * Handles click for the close icon button within the `Modal`. */ onClose() { this.closeSelect.emit(); } } ModalHeader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalHeader, deps: [{ token: i1$2.I18n }], target: i0.ɵɵFactoryTarget.Component }); ModalHeader.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: { theme: "theme", closeLabel: "closeLabel", showCloseButton: "showCloseButton" }, outputs: { closeSelect: "closeSelect" }, ngImport: i0, template: ` <header class="cds--modal-header {{theme}}"> <ng-content></ng-content> <div class="cds--modal-close-button"> <cds-icon-button *ngIf="showCloseButton" type="button" [buttonNgClass]="buttonNgClass" [buttonAttributes]="buttonAttributes" align="left" [description]="closeLabel" (click)="onClose()"> <svg cdsIcon="close" size="20" class="cds--modal-close__icon"></svg> </cds-icon-button> </div> </header> `, isInline: true, dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.IconButton, selector: "cds-icon-button, ibm-icon-button", inputs: ["buttonNgClass", "buttonAttributes", "buttonId", "kind", "size", "type", "isExpressive", "disabled", "description", "showTooltipWhenDisabled"], outputs: ["click", "focus", "blur", "tooltipClick"] }, { kind: "directive", type: i4.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalHeader, decorators: [{ type: Component, args: [{ selector: "cds-modal-header, ibm-modal-header", template: ` <header class="cds--modal-header {{theme}}"> <ng-content></ng-content> <div class="cds--modal-close-button"> <cds-icon-button *ngIf="showCloseButton" type="button" [buttonNgClass]="buttonNgClass" [buttonAttributes]="buttonAttributes" align="left" [description]="closeLabel" (click)="onClose()"> <svg cdsIcon="close" size="20" class="cds--modal-close__icon"></svg> </cds-icon-button> </div> </header> ` }] }], ctorParameters: function () { return [{ type: i1$2.I18n }]; }, propDecorators: { theme: [{ type: Input }], closeLabel: [{ type: Input }], showCloseButton: [{ type: Input }], closeSelect: [{ type: Output }] } }); class ModalFooter { } ModalFooter.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalFooter, deps: [], target: i0.ɵɵFactoryTarget.Component }); ModalFooter.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: ModalFooter, selector: "cds-modal-footer, ibm-modal-footer", ngImport: i0, template: ` <footer class="cds--modal-footer"> <ng-content></ng-content> </footer> `, isInline: true }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalFooter, decorators: [{ type: Component, args: [{ selector: "cds-modal-footer, ibm-modal-footer", template: ` <footer class="cds--modal-footer"> <ng-content></ng-content> </footer> ` }] }] }); class ModalContent { constructor() { this.modalContentClass = true; /** * Provide whether the modal content has a form element. * If `true` is used here, non-form child content should have `cds--modal-content__regular-content` class. */ this.hasForm = false; } } ModalContent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalContent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); ModalContent.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.3.0", type: ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: { hasForm: "hasForm" }, host: { properties: { "class.cds--modal-content": "this.modalContentClass", "class.cds--modal-content--with-form": "this.hasForm" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalContent, decorators: [{ type: Directive, args: [{ selector: "[cdsModalContent], [ibmModalContent]" }] }], propDecorators: { modalContentClass: [{ type: HostBinding, args: ["class.cds--modal-content"] }], hasForm: [{ type: HostBinding, args: ["class.cds--modal-content--with-form"] }, { type: Input }] } }); class ModalHeaderHeading { constructor() { this.modalHeaderHeadingClass = true; } } ModalHeaderHeading.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalHeaderHeading, deps: [], target: i0.ɵɵFactoryTarget.Directive }); ModalHeaderHeading.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.3.0", type: ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]", host: { properties: { "class.cds--modal-header__heading": "this.modalHeaderHeadingClass" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalHeaderHeading, decorators: [{ type: Directive, args: [{ selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }] }], propDecorators: { modalHeaderHeadingClass: [{ type: HostBinding, args: ["class.cds--modal-header__heading"] }] } }); class ModalHeaderLabel { constructor() { this.modalHeaderLabelClass = true; } } ModalHeaderLabel.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalHeaderLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive }); ModalHeaderLabel.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.3.0", type: ModalHeaderLabel, selector: "[cdsModalHeaderLabel], [ibmModalHeaderLabel]", host: { properties: { "class.cds--modal-header__label": "this.modalHeaderLabelClass" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalHeaderLabel, decorators: [{ type: Directive, args: [{ selector: "[cdsModalHeaderLabel], [ibmModalHeaderLabel]" }] }], propDecorators: { modalHeaderLabelClass: [{ type: HostBinding, args: ["class.cds--modal-header__label"] }] } }); /** * Component to create standard modals for presenting content or asking for user's input. * It can show as a passive modal showing only text or show as a transactional modal with * multiple buttons for different actions for the user to choose from. * * Using a modal in your application requires `cds-placeholder` which would generally be * placed near the end of your app component template (app.component.ts or app.component.html) as: * * ```html * <cds-placeholder></cds-placeholder> * ``` * * Example of opening the modal: * * ```typescript * \@Component({ * selector: "app-modal-demo", * template: ` * <button class="btn--primary" (click)="openModal()">Open modal</button> * <cds-placeholder></cds-placeholder>` * }) * export class ModalDemo { * openModal() { * this.modalService.show({ * modalType: "default", * label: "optional header text", * title: "Modal title", * text: "Modal text", * buttons: [{ * text: "Button text", * type: "primary", * click: clickFunction * }] * }); * } * } * ``` */ class AlertModal extends BaseModal { /** * Creates an instance of `AlertModal`. */ constructor(type = "default", label, title, content, size, hasScrollingContent = null, buttons = [], onClose, showCloseButton = true) { super(); this.type = type; this.label = label; this.title = title; this.content = content; this.size = size; this.hasScrollingContent = hasScrollingContent; this.buttons = buttons; this.onClose = onClose; this.showCloseButton = showCloseButton; for (let i = 0; i < this.buttons.length; i++) { const button = this.buttons[i]; if (!button.id) { button.id = `alert-modal-button-${i}`; } if (!button.type) { button.type = "secondary"; } } } ngAfterViewInit() { if (!this.modalContent) { return false; } const element = this.modalContent.nativeElement; if (element.scrollHeight > element.clientHeight) { element.tabIndex = 0; } else { element.tabIndex = -1; } } buttonClicked(buttonIndex) { const button = this.buttons[buttonIndex]; if (button.click) { button.click(); } this.closeModal(); } dismissModal(trigger) { if (this.onClose && this.onClose(trigger) === false) { return; } this.closeModal(); } } AlertModal.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: AlertModal, deps: [{ token: "type", optional: true }, { token: "label", optional: true }, { token: "title", optional: true }, { token: "content", optional: true }, { token: "size", optional: true }, { token: "hasScrollingContent", optional: true }, { token: "buttons", optional: true }, { token: "close", optional: true }, { token: "showCloseButton", optional: true }], target: i0.ɵɵFactoryTarget.Component }); AlertModal.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: AlertModal, selector: "cds-alert-modal, ibm-alert-modal", viewQueries: [{ propertyName: "modalContent", first: true, predicate: ["modalContent"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: ` <cds-modal [size]="size" [theme]="type" [ariaLabel]="title" [hasScrollingContent]="hasScrollingContent" [open]="open" (overlaySelected)="dismissModal('overlay')"> <cds-modal-header (closeSelect)="dismissModal('close')" [showCloseButton]="showCloseButton"> <p cdsModalHeaderLabel class="cds--type-delta">{{label}}</p> <p cdsModalHeaderHeading class="cds--type-beta">{{title}}</p> </cds-modal-header> <div cdsModalContent #modalContent> <p [innerHTML]="content"></p> </div> <cds-modal-footer *ngIf="buttons.length > 0"> <ng-container *ngFor="let button of buttons; let i = index"> <button [cdsButton]="button.type" (click)="buttonClicked(i)" [id]="button.id" [attr.modal-primary-focus]="(button.type.indexOf('primary') !== -1 ? '' : null)"> {{button.text}} </button> </ng-container> </cds-modal-footer> </cds-modal> `, isInline: true, dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "component", type: Modal, selector: "cds-modal, ibm-modal", inputs: ["size", "theme", "ariaLabel", "open", "trigger", "hasScrollingContent"], outputs: ["overlaySelected", "close"] }, { kind: "component", type: ModalHeader, selector: "cds-modal-header, ibm-modal-header", inputs: ["theme", "closeLabel", "showCloseButton"], outputs: ["closeSelect"] }, { kind: "component", type: ModalFooter, selector: "cds-modal-footer, ibm-modal-footer" }, { kind: "directive", type: ModalContent, selector: "[cdsModalContent], [ibmModalContent]", inputs: ["hasForm"] }, { kind: "directive", type: ModalHeaderHeading, selector: "[cdsModalHeaderHeading], [ibmModalHeaderHeading]" }, { kind: "directive", type: ModalHeaderLabel, selector: "[cdsModalHeaderLabel], [ibmModalHeaderLabel]" }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: AlertModal, decorators: [{ type: Component, args: [{ selector: "cds-alert-modal, ibm-alert-modal", template: ` <cds-modal [size]="size" [theme]="type" [ariaLabel]="title" [hasScrollingContent]="hasScrollingContent" [open]="open" (overlaySelected)="dismissModal('overlay')"> <cds-modal-header (closeSelect)="dismissModal('close')" [showCloseButton]="showCloseButton"> <p cdsModalHeaderLabel class="cds--type-delta">{{label}}</p> <p cdsModalHeaderHeading class="cds--type-beta">{{title}}</p> </cds-modal-header> <div cdsModalContent #modalContent> <p [innerHTML]="content"></p> </div> <cds-modal-footer *ngIf="buttons.length > 0"> <ng-container *ngFor="let button of buttons; let i = index"> <button [cdsButton]="button.type" (click)="buttonClicked(i)" [id]="button.id" [attr.modal-primary-focus]="(button.type.indexOf('primary') !== -1 ? '' : null)"> {{button.text}} </button> </ng-container> </cds-modal-footer> </cds-modal> ` }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: ["type"] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: ["label"] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: ["title"] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: ["content"] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: ["size"] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: ["hasScrollingContent"] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: ["buttons"] }] }, { type: Function, decorators: [{ type: Optional }, { type: Inject, args: ["close"] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: ["showCloseButton"] }] }]; }, propDecorators: { modalContent: [{ type: ViewChild, args: ["modalContent", { static: true }] }] } }); var AlertModalType; (function (AlertModalType) { AlertModalType["default"] = "default"; AlertModalType["danger"] = "danger"; })(AlertModalType || (AlertModalType = {})); var ModalButtonType; (function (ModalButtonType) { ModalButtonType["primary"] = "primary"; ModalButtonType["secondary"] = "secondary"; ModalButtonType["tertiary"] = "tertiary"; ModalButtonType["ghost"] = "ghost"; ModalButtonType["danger"] = "danger"; ModalButtonType["danger_primary"] = "danger--primary"; })(ModalButtonType || (ModalButtonType = {})); /** * Extends Base Modal Service to create Alert Modal with a function call. Placed in a seperate service * to prevent remote scoping (NG3003) which has side effects. Hence, import cycles are not allowed when * compilationMode is set to `partial`. * * * Modal service handles instantiating and destroying modal instances. * Uses PlaceholderService to track open instances, and for it's placeholder view reference. */ class ModalService extends BaseModalService { /** * Creates an instance of `ModalService`. */ constructor(placeholderService) { super(placeholderService); this.placeholderService = placeholderService; } /** * Creates and renders a new alert modal component. * @param data You can pass in: * `type` - "default" | "danger" = "default", * `label` - a label shown over the title, * `title` - modal's title, * `content` - modal's content, could include HTML tags. * `buttons` is an array of objects * `close` custom close function * ``` * { * text: "Button text", * type: "primary" | "secondary" | "tertiary" | "ghost" | "danger" | "danger--primary" = "primary", * click: clickFunction, * } * ``` */ show(data) { return this.create({ component: AlertModal, inputs: { type: data.type, label: data.label, title: data.title, content: data.content, hasScrollingContent: data.hasScrollingContent !== undefined ? data.hasScrollingContent : null, size: data.size, buttons: data.buttons || [], close: data.close || (() => { }), showCloseButton: data.showCloseButton } }); } } ModalService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalService, deps: [{ token: i1.PlaceholderService }], target: i0.ɵɵFactoryTarget.Injectable }); ModalService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i1.PlaceholderService }]; } }); class ModalContentText { constructor() { this.modalContentTextClass = true; } } ModalContentText.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalContentText, deps: [], target: i0.ɵɵFactoryTarget.Directive }); ModalContentText.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.3.0", type: ModalContentText, selector: "[cdsModalContentText], [ibmModalContentText]", host: { properties: { "class.cds--modal-content__text": "this.modalContentTextClass" } }, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalContentText, decorators: [{ type: Directive, args: [{ selector: "[cdsModalContentText], [ibmModalContentText]" }] }], propDecorators: { modalContentTextClass: [{ type: HostBinding, args: ["class.cds--modal-content__text"] }] } }); // modules class ModalModule { } ModalModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); ModalModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: ModalModule, declarations: [AlertModal, Modal, ModalHeader, ModalFooter, Overlay, ModalContent, ModalContentText, ModalHeaderHeading, ModalHeaderLabel, BaseModal], imports: [CommonModule, ButtonModule, I18nModule, PlaceholderModule, ExperimentalModule, IconModule], exports: [AlertModal, Modal, ModalHeader, ModalFooter, ModalContent, ModalContentText, ModalHeaderHeading, ModalHeaderLabel, BaseModal] }); ModalModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalModule, providers: [BaseModalService, ModalService], imports: [CommonModule, ButtonModule, I18nModule, PlaceholderModule, ExperimentalModule, IconModule] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: ModalModule, decorators: [{ type: NgModule, args: [{ declarations: [ AlertModal, Modal, ModalHeader, ModalFooter, Overlay, ModalContent, ModalContentText, ModalHeaderHeading, ModalHeaderLabel, BaseModal ], exports: [ AlertModal, Modal, ModalHeader, ModalFooter, ModalContent, ModalContentText, ModalHeaderHeading, ModalHeaderLabel, BaseModal ], providers: [BaseModalService, ModalService], imports: [ CommonModule, ButtonModule, I18nModule, PlaceholderModule, ExperimentalModule, IconModule ] }] }] }); /** * Generated bundle index. Do not edit. */ export { AlertModal, AlertModalType, BaseModal, BaseModalService, Modal, ModalButtonType, ModalContent, ModalContentText, ModalFooter, ModalHeader, ModalHeaderHeading, ModalHeaderLabel, ModalModule, ModalService, Overlay }; //# sourceMappingURL=carbon-components-angular-modal.mjs.map