ember-flash-notifications

Overview

The addon is designed to display flash notifications within Ember.js applications.

It utilizes Popover API which works well in Chrome-based browsers but is not fully supported in Firefox, at this moment @starting-style rule has not been implemented yet.

Installation

ember install ember-flash-notifications

Usage

Minimal implementation

<FlashNotifications as |notification|>
  <FlashNotification @notification={{notification}}>
    <div class="error">{{notification.message}}</div>
  </FlashNotification>
</FlashNotifications>
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';

export default class FlashComponent extends Component {
  @service notifications;

  @action
  addNotification() {
    this.notifications.error('Something is going wrong!');
  }
}
[popover] {
  @apply my-0 mr-0;
}

.flash {
  @apply w-80 transition-opacity block opacity-0;
}

.flash:popover-open {
  @apply opacity-100;
}

.flash .error {
  @apply bg-rose-500 text-white p-2 rounded-sm;
}

@starting-style {
  .flash:popover-open {
    @apply opacity-0;
  }
}

Different notification types

<FlashNotifications as |notification|>
  <FlashNotification @notification={{notification}}>
    <div class={{notification.type}}>{{notification.message}}</div>
  </FlashNotification>
</FlashNotifications>
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { htmlSafe } from '@ember/template';
import { inject as service } from '@ember/service';

export default class FlashComponent extends Component {
  @service notifications;

  @action
  errorNotification() {
    this.notifications.error('Something is going wrong!');
  }

  @action
  warningNotification() {
    this.notifications.warning('Doubtful but OK');
  }

  @action
  successNotification() {
    this.notifications.success('Working as expected');
  }

  @action
  infoNotification() {
    this.notifications.info(htmlSafe('Some useful info splitted<br>to multiple lines'));
  }
}
[popover] {
  @apply my-0 mr-0;
}

.flash {
  @apply w-80 transition-opacity block opacity-0;
}

.flash:popover-open {
  @apply opacity-100;
}

.flash div {
  @apply text-white p-2 rounded-sm;
}

.flash div.error {
  @apply bg-rose-500;
}

.flash div.warning {
  @apply bg-orange-500;
}

.flash div.success {
  @apply bg-lime-500;
}

.flash div.info {
  @apply bg-sky-500;
}

@starting-style {
  .flash:popover-open {
    @apply opacity-0;
  }
}

Custom notifications

ember g instance-initializer notifications

 

export function initialize(owner) {
  let notifications = owner.lookup('service:notifications');

  notifications.registerShorthand('custom');
}

export default {
  initialize,
};
<FlashNotifications as |notification|>
  <FlashNotification class={{notification.type}} @notification={{notification}}>
    <div class="p-1 bg-transparent">
      <div class="flash-content">
        {{#if notification.isCustom}}
          <div class="text-sm font-bold text-slate-500 mr-4">{{notification.options.title}}</div>
        {{/if}}
        <div class="flex gap-4">
          <div class="flash-type">
            <img class="w-16 h-16" src="biohazard.svg" alt="Notification type icon" />
          </div>
          <div>
            <div class="flex flex-col grow">
              <span class="text-xs">{{notification.message}}</span>
              <code class="text-xs text-slate-400">{{notification.options.error}}</code>
            </div>
          </div>
        </div>

        <div class="absolute top-1 right-1 flex-shrink-0 flex">
          <div role="button" name="close" class="flash-close text-gray-400">
            <span class="sr-only">Close</span>
            <Icons::FasTimes role="presentation" class="h-4 w-4" />
          </div>
        </div>
      </div>
    </div>
  </FlashNotification>
</FlashNotifications>
import { action } from '@ember/object';
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';

export default class FlashCustomComponent extends Component {
  @service notifications;

  @action
  addNotification() {
    this.notifications.custom('Error in DNA sequence:', {
      duration: 10_000,
      title: 'Custom notification message',
      error:
        'MDSKGSSQKGSRLLLLLVVSNLLLCQGVVSTPVCPNGPGNCQVSLRDLFDRAVMVSHYIHDLSS
EMFNEFDKRYAQGKGFITMALNSCHTSSLPTPEDKEQAQQTHHEVLMSLILGLLRSWNDPLYHL
VTEVRGMKGAPDAILSRAIEIEEENKRLLEGMEMIFGQVIPGAKETEPYPVWSGLPSLQTKDED
ARYSAFYNLLHCLRRDSSKIDTYLKLLNCRIIYNNNC*',
    });
  }
}

Custom duration

By default, the time of notification disappearing is 5s. Setting the duration to 0 makes the notification persistent.
This value can be overridden using initializer:

ember g instance-initializer notifications

 

export function initialize(owner) {
  let notifications = owner.lookup('service:notifications');
  notifications.setOptions({
    duration: 3_000,
  });
}

export default {
  initialize,
};

You can also specify the duration for a particular notification.

import { action } from '@ember/object';
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';

export default class FlashComponent extends Component {
  @service notifications;

  @action
  errorNotification() {
    this.notifications.error('This notification will be visible until you close it', { duration: 0 });
  }

  @action
  warningNotification() {
    this.notifications.warning('Disappeared in 10 seconds', { duration: 10_000 });
  }
}

Manual closing a notification

You'll need to define an element with name="close" attribute:

<FlashNotifications as |notification|>
  <FlashNotification @notification={{notification}}>
    <div class="relative info">
      {{notification.message}}
      <div class="absolute top-1 right-1 flex-shrink-0 flex">
        <div role="button" name="close" class="flash-close">
          <span class="sr-only">Close</span>
          <Icons::FasTimes role="presentation" class="h-4 w-4" />
        </div>
      </div>
    </div>
  </FlashNotification>
</FlashNotifications>

Clear all notifications

Note: when fired, no any transitions and no onClose calls will be applied.

import { action } from '@ember/object';
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';

export default class FlashComponent extends Component {
  @service notifications;

  @action
  clearNotifications() {
    this.notifications.clear();
  }
}

Custom onClose handler

Fired on CSS transition ends

<FlashNotifications as |notification|>
  <FlashNotification @notification={{notification}} @onClose={{this.onClose}}>
    <div class="relative info">
      {{notification.message}}
      <div class="absolute top-1 right-1 flex-shrink-0 flex">
        <div role="button" name="close" class="flash-close">
          <span class="sr-only">Close</span>
          <Icons::FasTimes role="presentation" class="h-4 w-4" />
        </div>
      </div>
    </div>
  </FlashNotification>
</FlashNotifications>
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';

export default class FlashCloseComponent extends Component {
  @service notifications;

  onClose(notification) {
    window.alert('Notification  was closed');
  }
}

API Reference

Notifications service

setOptions(options)

Sets the default options for notifications by merging with existing defaults.

  • options (Object): Custom options to be merged with the default options.
registerShorthand(type)

Registers a shorthand method for creating notifications of a specific type and adds a getter property (is<Type>) to the Notification prototype.

type (String): The type of notification to register.

error(message, options) warning(message, options) success(message, options) info(message, options)

Adds a error (warning, success, of info notification appropriately) notification to the queue.

  • message (String): The message to be displayed.
  • options (Object): Instance-specific options for the notification.
remove(notification)

Removes a specific notification from the queue.

notification (Object): The notification object to be removed.

clear()

Clears all notifications from the queue.

Components

FlashNotifications

FlashNotifications component is a container that manages and displays a list of notification messages. It yields each notification to be rendered within FlashNotification component.

FlashNotification

The FlashNotification component is responsible for rendering individual notification messages.

Arguments:

  • notification (Object): Notification object to be displayed.
  • onClose (function): A callback function to be invoked when the notification is closed.
<FlashNotifications as |notification|>
  <FlashNotification @notification={{notification}} @onClose={{this.onClose}}>
    <div class="relative info">
      {{notification.message}}
      <div class="absolute top-1 right-1 flex-shrink-0 flex">
        <div role="button" name="close" class="flash-close">
          <span class="sr-only">Close</span>
          <Icons::FasTimes role="presentation" class="h-4 w-4" />
        </div>
      </div>
    </div>
  </FlashNotification>
</FlashNotifications>
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';

export default class FlashCloseComponent extends Component {
  @service notifications;

  onClose(notification) {
    window.alert('Notification  was closed');
  }
}