UNPKG

@hashicorp/design-system-components

Version:
146 lines (143 loc) 6.2 kB
import Component from '@glimmer/component'; import { assert } from '@ember/debug'; import { guidFor } from '@ember/object/internals'; import { tracked } from '@glimmer/tracking'; import { eq } from 'ember-truth-helpers'; import { hash } from '@ember/helper'; import { on } from '@ember/modifier'; import didInsert from '@ember/render-modifiers/modifiers/did-insert'; import { HdsAlertColorValues, HdsAlertTypeValues } from './types.js'; import HdsDismissButton from '../dismiss-button/index.js'; import HdsIcon from '../icon/index.js'; import HdsAlertTitle from './title.js'; import HdsAlertDescription from './description.js'; import HdsButton from '../button/index.js'; import HdsLinkStandalone from '../link/standalone.js'; import HdsYield from '../yield/index.js'; import { precompileTemplate } from '@ember/template-compilation'; import { setComponentTemplate } from '@ember/component'; import { g, i } from 'decorator-transforms/runtime'; /** * Copyright IBM Corp. 2021, 2025 * SPDX-License-Identifier: MPL-2.0 */ const TYPES = Object.values(HdsAlertTypeValues); const DEFAULT_COLOR = HdsAlertColorValues.Neutral; const COLORS = Object.values(HdsAlertColorValues); const MAPPING_COLORS_TO_ICONS = { [HdsAlertColorValues.Neutral]: 'info', [HdsAlertColorValues.Highlight]: 'info', [HdsAlertColorValues.Success]: 'check-circle', [HdsAlertColorValues.Warning]: 'alert-triangle', [HdsAlertColorValues.Critical]: 'alert-diamond' }; const CONTENT_ELEMENT_SELECTOR = '.hds-alert__content'; const TITLE_ELEMENT_SELECTOR = '.hds-alert__title'; const DESCRIPTION_ELEMENT_SELECTOR = '.hds-alert__description'; class HdsAlert extends Component { static { g(this.prototype, "_role", [tracked]); } #_role = (i(this, "_role"), void 0); static { g(this.prototype, "_ariaLabelledBy", [tracked]); } #_ariaLabelledBy = (i(this, "_ariaLabelledBy"), void 0); constructor(owner, args) { super(owner, args); assert(`@type for "Hds::Alert" must be one of the following: ${TYPES.join(', ')}; received: ${this.args.type}`, TYPES.includes(this.args.type)); } get color() { const { color = DEFAULT_COLOR } = this.args; assert(`@color for "Hds::Alert" must be one of the following: ${COLORS.join(', ')}; received: ${color}`, COLORS.includes(color)); return color; } get icon() { const { icon } = this.args; // If `icon` isn't passed, use the pre-defined one from `color` if (icon === undefined) { if (this.args.type === 'compact') { // for the "compact" type by default we use filled icons return `${MAPPING_COLORS_TO_ICONS[this.color]}-fill`; } else { // for all the other types by default we use outlined icons return MAPPING_COLORS_TO_ICONS[this.color]; } // If `icon` is set explicitly to false, user doesn't want any icon in the alert } else if (icon === false) { assert(`@icon for "Hds::Alert" with @type "compact" is required`, this.args.type !== 'compact'); return false; } else { // If a name for `icon` is passed, set HdsIcon to that name return icon; } } // eslint-disable-next-line @typescript-eslint/no-explicit-any get onDismiss() { const { onDismiss } = this.args; if (typeof onDismiss === 'function') { return onDismiss; } else { return false; } } get iconSize() { if (this.args.type === 'compact') { return '16'; } else { return '24'; } } get classNames() { const classes = ['hds-alert']; // Add a class based on the @type argument classes.push(`hds-alert--type-${this.args.type}`); // Add a class based on the @color argument classes.push(`hds-alert--color-${this.color}`); return classes.join(' '); } didInsert = element => { const actions = element.querySelectorAll(`${CONTENT_ELEMENT_SELECTOR} button, ${CONTENT_ELEMENT_SELECTOR} a`); // an Alert which actually alerts users (has role="alert" & aria-live="polite") as opposed to an informational or promo "alert" const isSemanticAlert = this.color === 'warning' || this.color === 'critical' || this.color === 'success'; if (isSemanticAlert && actions.length) { this._role = 'alertdialog'; } else if (isSemanticAlert) { this._role = 'alert'; } // `alertdialog` must have an accessible name so we use either the // title or the description as label for the alert const label = element.querySelector(TITLE_ELEMENT_SELECTOR) || element.querySelector(DESCRIPTION_ELEMENT_SELECTOR); if (label) { const labelId = label.getAttribute('id') || guidFor(element); label.setAttribute('id', labelId); this._ariaLabelledBy = labelId; } }; static { setComponentTemplate(precompileTemplate("<div class={{this.classNames}} role={{this._role}} aria-live={{if this._role \"polite\"}} aria-labelledby={{this._ariaLabelledBy}} {{didInsert this.didInsert}} ...attributes>\n {{#if this.icon}}\n <div class=\"hds-alert__icon\">\n <HdsIcon @name={{this.icon}} @size={{this.iconSize}} @stretched={{true}} />\n </div>\n {{/if}}\n\n <div class=\"hds-alert__content\">\n <div class=\"hds-alert__text\n {{if (eq @type \"compact\") \"hds-typography-body-100\" \"hds-typography-body-200\"}}\">\n {{yield (hash Title=HdsAlertTitle)}}\n {{yield (hash Description=HdsAlertDescription)}}\n </div>\n\n <div class=\"hds-alert__actions\">\n {{yield (hash Button=(component HdsButton size=\"small\") LinkStandalone=(component HdsLinkStandalone size=\"small\"))}}\n </div>\n {{yield (hash Generic=HdsYield)}}\n </div>\n\n {{#if this.onDismiss}}\n <HdsDismissButton class=\"hds-alert__dismiss\" {{on \"click\" this.onDismiss}} />\n {{/if}}\n</div>", { strictMode: true, scope: () => ({ didInsert, HdsIcon, eq, hash, HdsAlertTitle, HdsAlertDescription, HdsButton, HdsLinkStandalone, HdsYield, HdsDismissButton, on }) }), this); } } export { COLORS, DEFAULT_COLOR, MAPPING_COLORS_TO_ICONS, TYPES, HdsAlert as default }; //# sourceMappingURL=index.js.map