import { action } from '@ember/object';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { later } from '@ember/runloop';
import usesTransition from 'ember-bootstrap/utils/decorators/uses-transition';
import deprecateSubclassing from 'ember-bootstrap/utils/deprecate-subclassing';
import arg from 'ember-bootstrap/utils/decorators/arg';
import { localCopy } from 'tracked-toolbox';
/**
Implements [Bootstrap alerts](http://getbootstrap.com/components/#alerts)
### Usage
By default, it is a user dismissible alert with a fade out animation, both of which can be disabled. Be sure to set the
`type` property for proper styling.
```hbs
<BsAlert @type="success">
<strong>Well done!</strong> You successfully read this important alert message.
</BsAlert>
```
Optionally you can render a header for the alert using named blocks syntax:
```hbs
<BsAlert>
<:header>
Well done!
</:header>
<:body>
You successfully read this important alert message.
</:body>
</BsAlert>
```
The header is rendered using a `<h4>` element by default. You can customize
that one by setting `@headerTag` argument of `<BsAlert>`.
Using named block syntax as shown above may require installing
[ember-named-blocks-polyfill](https://github.com/ember-polyfills/ember-named-blocks-polyfill)
in your project depending on the Ember version used.
*Note that only invoking the component in a template as shown above is considered part of its public API. Extending from it (subclassing) is generally not supported, and may break at any time.*
@class Alert
@namespace Components
@extends Glimmer.Component
@public
*/
@deprecateSubclassing
export default class Alert extends Component {
/**
* A dismissible alert will have a close button in the upper right corner, that the user can click to dismiss
* the alert.
*
* @property dismissible
* @type boolean
* @default true
* @public
*/
@arg
dismissible = true;
/**
* If true the alert is completely hidden. Will be set when the fade animation has finished.
*
* @property hidden
* @type boolean
* @default false
* @readonly
* @private
*/
@tracked
hidden = !this.visible;
/**
* This is an unfortunate duplication of the previous property, but this is untracked to avoid causing the dreaded "same computation" assertion in GlimmerVM when reading a tracked property before setting it.
* @private
*/
_hidden = !this.visible;
/**
* This property controls if the alert should be visible. If false it might still be in the DOM until the fade animation
* has completed.
*
* When the alert is dismissed by user interaction this property will not update by using two-way bindings in order
* to follow DDAU best practices. If you want to react to such changes, subscribe to the `onDismiss` action
*
* @property visible
* @type boolean
* @default true
* @public
*/
@arg
visible = true;
/**
* @property _visible
* @private
*/
@localCopy('visible')
_visible;
/**
* Set to false to disable the fade out animation when hiding the alert.
*
* @property fade
* @type boolean
* @default true
* @public
*/
@arg
fade = true;
get showAlert() {
return this._visible && this.args.fade !== false;
}
/**
* The duration of the fade out animation
*
* @property fadeDuration
* @type number
* @default 150
* @public
*/
@arg
fadeDuration = 150;
/**
* Property for type styling
*
* For the available types see the [Bootstrap docs](https://getbootstrap.com/docs/4.3/components/alerts/)
*
* @property type
* @type String
* @public
*/
/**
* Use CSS transitions?
*
* @property usesTransition
* @type boolean
* @readonly
* @private
*/
@usesTransition('fade')
usesTransition;
/**
* The action to be sent after the alert has been dismissed (including the CSS transition).
*
* @event onDismissed
* @public
*/
/**
* The action is called when the close button is clicked.
*
* You can return false to prevent closing the alert automatically, and do that in your action by
* setting `visible` to false.
*
* @event onDismiss
* @public
*/
@action
dismiss() {
if (this.args.onDismiss?.() !== false) {
this._visible = false;
}
}
/**
* Call to make the alert visible again after it has been hidden
*
* @method show
* @private
*/
show() {
this.hidden = this._hidden = false;
}
/**
* Call to hide the alert. If the `fade` property is true, this will fade out the alert before being finally
* dismissed.
*
* @method hide
* @private
*/
hide() {
if (this._hidden) {
return;
}
if (this.usesTransition) {
later(
this,
function () {
if (!this.isDestroyed) {
this.hidden = this._hidden = true;
this.args.onDismissed?.();
}
},
this.fadeDuration
);
} else {
this.hidden = this._hidden = true;
this.args.onDismissed?.();
}
}
@action
showOrHide() {
if (this._visible) {
this.show();
} else {
this.hide();
}
}
}