├── app
├── .gitkeep
├── services
│ └── notifier.js
├── utils
│ └── default-value.js
├── components
│ ├── ember-notifier
│ │ └── component.js
│ └── ember-notifier-notification
│ │ ├── component.js
│ │ ├── close
│ │ └── component.js
│ │ ├── icon
│ │ └── component.js
│ │ └── content
│ │ └── component.js
└── styles
│ └── ember-notifier.scss
├── addon
├── .gitkeep
├── components
│ ├── ember-notifier-notification
│ │ ├── icon
│ │ │ ├── template.hbs
│ │ │ └── component.js
│ │ ├── close
│ │ │ ├── template.hbs
│ │ │ └── component.js
│ │ ├── content
│ │ │ ├── template.hbs
│ │ │ └── component.js
│ │ ├── template.hbs
│ │ └── component.js
│ └── ember-notifier
│ │ ├── template.hbs
│ │ └── component.js
├── utils
│ └── default-value.js
├── mixins
│ └── swipe-support.js
└── services
│ └── notifier.js
├── vendor
└── .gitkeep
├── tests
├── helpers
│ ├── .gitkeep
│ └── trigger-swipe-event.js
├── unit
│ ├── .gitkeep
│ ├── mixins
│ │ └── swipe-support-test.js
│ ├── utils
│ │ └── default-value-test.js
│ └── services
│ │ └── notifier-test.js
├── integration
│ ├── .gitkeep
│ └── components
│ │ ├── ember-notifier-notification
│ │ ├── icon
│ │ │ └── component-test.js
│ │ ├── close
│ │ │ └── component-test.js
│ │ ├── content
│ │ │ └── component-test.js
│ │ └── component-test.js
│ │ └── ember-notifier
│ │ └── component-test.js
├── dummy
│ ├── app
│ │ ├── helpers
│ │ │ ├── .gitkeep
│ │ │ └── capitalize.js
│ │ ├── components
│ │ │ ├── .gitkeep
│ │ │ ├── tailwind-alert
│ │ │ │ ├── template.hbs
│ │ │ │ └── component.js
│ │ │ ├── notification-icon
│ │ │ │ ├── template.hbs
│ │ │ │ └── component.js
│ │ │ ├── addon-dynamic-demo
│ │ │ │ ├── template.hbs
│ │ │ │ └── component.js
│ │ │ ├── task-message
│ │ │ │ ├── template.hbs
│ │ │ │ └── component.js
│ │ │ └── addon-demo
│ │ │ │ ├── component.js
│ │ │ │ └── template.hbs
│ │ ├── resolver.js
│ │ ├── templates
│ │ │ └── .gitignore
│ │ ├── not-found
│ │ │ ├── route.js
│ │ │ └── template.hbs
│ │ ├── docs
│ │ │ ├── icons
│ │ │ │ ├── snippets
│ │ │ │ │ ├── fa-svg-component.hbs
│ │ │ │ │ ├── fa-svg-application.hbs
│ │ │ │ │ ├── fa-svg-component.js
│ │ │ │ │ ├── fa-web-font-config-environment.js
│ │ │ │ │ └── fa-svg-config-environment.js
│ │ │ │ └── template.md
│ │ │ ├── styles
│ │ │ │ ├── snippets
│ │ │ │ │ ├── bulma-config-environment.js
│ │ │ │ │ ├── style-sass.scss
│ │ │ │ │ ├── spectre-config-environment.js
│ │ │ │ │ ├── zurb-config-environment.js
│ │ │ │ │ ├── bootstrap-config-environment.js
│ │ │ │ │ ├── bulma-application.hbs
│ │ │ │ │ ├── spectre-application.hbs
│ │ │ │ │ ├── zurb-application.hbs
│ │ │ │ │ └── bootstrap-application.hbs
│ │ │ │ └── template.md
│ │ │ ├── demo
│ │ │ │ └── template.md
│ │ │ ├── touch
│ │ │ │ ├── snippets
│ │ │ │ │ └── touch-config-environment.js
│ │ │ │ └── template.md
│ │ │ ├── animation
│ │ │ │ ├── snippets
│ │ │ │ │ └── animation-config-environment.js
│ │ │ │ └── template.md
│ │ │ ├── index
│ │ │ │ └── template.md
│ │ │ ├── template.hbs
│ │ │ ├── options
│ │ │ │ ├── snippets
│ │ │ │ │ ├── notification-options.js
│ │ │ │ │ └── global-options.js
│ │ │ │ └── template.md
│ │ │ ├── dynamic
│ │ │ │ └── template.md
│ │ │ └── usage
│ │ │ │ └── template.md
│ │ ├── services
│ │ │ └── demo.js
│ │ ├── application
│ │ │ ├── controller.js
│ │ │ └── template.hbs
│ │ ├── styles
│ │ │ └── app.scss
│ │ ├── app.js
│ │ ├── index
│ │ │ ├── template.hbs
│ │ │ └── index-content
│ │ │ │ └── template.md
│ │ ├── router.js
│ │ └── index.html
│ ├── config
│ │ ├── optional-features.json
│ │ ├── targets.js
│ │ └── environment.js
│ └── public
│ │ ├── robots.txt
│ │ ├── assets
│ │ └── images
│ │ │ ├── favicon.ico
│ │ │ ├── favicon.png
│ │ │ └── notification-demo.gif
│ │ └── crossdomain.xml
├── test-helper.js
└── index.html
├── .watchmanconfig
├── index.js
├── config
├── environment.js
├── addon-docs.js
├── deploy.js
└── ember-try.js
├── .template-lintrc.js
├── .ember-cli
├── .eslintignore
├── .editorconfig
├── .gitignore
├── .npmignore
├── testem.js
├── ember-cli-build.js
├── LICENSE.md
├── .eslintrc.js
├── .travis.yml
├── CHANGELOG.md
├── package.json
└── README.md
/app/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/addon/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/helpers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/unit/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/integration/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/helpers/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/tailwind-alert/template.hbs:
--------------------------------------------------------------------------------
1 | {{yield}}
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {
2 | "ignore_dirs": ["tmp", "dist"]
3 | }
4 |
--------------------------------------------------------------------------------
/app/services/notifier.js:
--------------------------------------------------------------------------------
1 | export { default } from 'ember-notifier/services/notifier';
2 |
--------------------------------------------------------------------------------
/tests/dummy/config/optional-features.json:
--------------------------------------------------------------------------------
1 | {
2 | "jquery-integration": false
3 | }
4 |
--------------------------------------------------------------------------------
/app/utils/default-value.js:
--------------------------------------------------------------------------------
1 | export { default } from 'ember-notifier/utils/default-value';
2 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | name: 'ember-notifier'
5 | };
6 |
--------------------------------------------------------------------------------
/tests/dummy/public/robots.txt:
--------------------------------------------------------------------------------
1 | # http://www.robotstxt.org
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/notification-icon/template.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{fa-icon icon}}
3 |
--------------------------------------------------------------------------------
/tests/dummy/app/resolver.js:
--------------------------------------------------------------------------------
1 | import Resolver from 'ember-resolver';
2 |
3 | export default Resolver;
4 |
--------------------------------------------------------------------------------
/app/components/ember-notifier/component.js:
--------------------------------------------------------------------------------
1 | export { default } from 'ember-notifier/components/ember-notifier/component';
--------------------------------------------------------------------------------
/tests/dummy/app/templates/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory
2 | *
3 | # Except this file
4 | !.gitignore
5 |
--------------------------------------------------------------------------------
/tests/dummy/app/not-found/route.js:
--------------------------------------------------------------------------------
1 | import Route from '@ember/routing/route';
2 |
3 | export default Route.extend({
4 | });
5 |
--------------------------------------------------------------------------------
/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(/* environment, appConfig */) {
4 | return { };
5 | };
6 |
--------------------------------------------------------------------------------
/app/components/ember-notifier-notification/component.js:
--------------------------------------------------------------------------------
1 | export { default } from 'ember-notifier/components/ember-notifier-notification/component';
--------------------------------------------------------------------------------
/tests/dummy/public/assets/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottwernervt/ember-notifier/HEAD/tests/dummy/public/assets/images/favicon.ico
--------------------------------------------------------------------------------
/tests/dummy/public/assets/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottwernervt/ember-notifier/HEAD/tests/dummy/public/assets/images/favicon.png
--------------------------------------------------------------------------------
/app/components/ember-notifier-notification/close/component.js:
--------------------------------------------------------------------------------
1 | export { default } from 'ember-notifier/components/ember-notifier-notification/close/component';
--------------------------------------------------------------------------------
/app/components/ember-notifier-notification/icon/component.js:
--------------------------------------------------------------------------------
1 | export { default } from 'ember-notifier/components/ember-notifier-notification/icon/component';
--------------------------------------------------------------------------------
/tests/dummy/app/docs/icons/snippets/fa-svg-component.hbs:
--------------------------------------------------------------------------------
1 | {{! BEGIN-SNIPPET fa-svg-component.hbs }}
2 | {{fa-icon icon}}
3 | {{! END-SNIPPET }}
--------------------------------------------------------------------------------
/app/components/ember-notifier-notification/content/component.js:
--------------------------------------------------------------------------------
1 | export { default } from 'ember-notifier/components/ember-notifier-notification/content/component';
--------------------------------------------------------------------------------
/tests/dummy/public/assets/images/notification-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/scottwernervt/ember-notifier/HEAD/tests/dummy/public/assets/images/notification-demo.gif
--------------------------------------------------------------------------------
/addon/components/ember-notifier-notification/icon/template.hbs:
--------------------------------------------------------------------------------
1 |
2 | {{#if hasBlock}}
3 | {{yield}}
4 | {{else}}
5 |
6 | {{/if}}
7 |
--------------------------------------------------------------------------------
/tests/dummy/app/services/demo.js:
--------------------------------------------------------------------------------
1 | import Service from '@ember/service';
2 |
3 | export default Service.extend({
4 | position: 'is-top',
5 | duration: 0,
6 | type: 'primary',
7 | });
8 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/icons/snippets/fa-svg-application.hbs:
--------------------------------------------------------------------------------
1 | {{! BEGIN-SNIPPET fa-svg-application.hbs }}
2 | {{ember-notifier iconComponent="notification-icon"}}
3 | {{outlet}}
4 | {{! END-SNIPPET }}
--------------------------------------------------------------------------------
/tests/dummy/app/not-found/template.hbs:
--------------------------------------------------------------------------------
1 |
2 |
Not found
3 |
This page doesn't exist. {{#link-to "index" class="docs-md__a"}}Head home?{{/link-to}}
4 |
5 |
--------------------------------------------------------------------------------
/tests/dummy/app/application/controller.js:
--------------------------------------------------------------------------------
1 | import Controller from '@ember/controller';
2 | import { inject as service } from '@ember/service';
3 |
4 | export default Controller.extend({
5 | demo: service(),
6 | });
7 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/styles/snippets/bulma-config-environment.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET bulma-config-environment.js
2 | let ENV = {
3 | emberNotifier: {
4 | 'secondaryClass': 'is-link',
5 | }
6 | }
7 | //! END-SNIPPET
8 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/notification-icon/component.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import layout from './template';
3 |
4 | export default Component.extend({
5 | layout,
6 |
7 | classNames: ['ember-notifier-icon'],
8 | });
9 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/icons/snippets/fa-svg-component.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET fa-svg-component.js
2 | import Component from '@ember/component';
3 |
4 | export default Component.extend({
5 | classNames: ['ember-notifier-icon'],
6 | });
7 | //! END-SNIPPET
8 |
--------------------------------------------------------------------------------
/tests/test-helper.js:
--------------------------------------------------------------------------------
1 | import Application from '../app';
2 | import config from '../config/environment';
3 | import { setApplication } from '@ember/test-helpers';
4 | import { start } from 'ember-qunit';
5 |
6 | setApplication(Application.create(config.APP));
7 |
8 | start();
9 |
--------------------------------------------------------------------------------
/tests/dummy/app/helpers/capitalize.js:
--------------------------------------------------------------------------------
1 | import { helper } from '@ember/component/helper';
2 | import { capitalize as _capitalize } from '@ember/string';
3 |
4 | export function capitalize([value]) {
5 | return _capitalize(value);
6 | }
7 |
8 | export default helper(capitalize);
9 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/demo/template.md:
--------------------------------------------------------------------------------
1 | # Demo
2 |
3 | {{addon-demo}}
4 |
5 | {{#tailwind-alert isInfo=true}}
6 | Notification Icons
7 |
8 | This demo is using [Font Awesome 5](https://fontawesome.com) for demonstration purposes.
9 | {{/tailwind-alert}}
10 |
--------------------------------------------------------------------------------
/.template-lintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | extends: 'recommended',
5 | rules: {
6 | 'attribute-indentation': {
7 | 'element-open-end': 'last-attribute',
8 | 'mustache-open-end': 'last-attribute',
9 | 'open-invocation-max-len': 120
10 | }
11 | }
12 | };
13 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/touch/snippets/touch-config-environment.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET touch-config-environment.js
2 | let ENV = {
3 | emberNotifier: {
4 | swipeDirection: 'right', // 'left' or 'right'
5 | minSwipeDistance: 150, // px
6 | maxSwipeTime: 300, // ms
7 | }
8 | };
9 | //! END-SNIPPET
10 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/styles/snippets/style-sass.scss:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET style-sass.scss
2 | $ember-notifier-z-index: 999999;
3 | $ember-notifier-text-color: #000000;
4 | $ember-notifier-primary-background-color: #5755D9;
5 | $ember-notifier-close-button-size: 1.25rem;
6 |
7 | @import 'ember-notifier';
8 | //! END-SNIPPET
9 |
--------------------------------------------------------------------------------
/.ember-cli:
--------------------------------------------------------------------------------
1 | {
2 | /**
3 | Ember CLI sends analytics information by default. The data is completely
4 | anonymous, but there are times when you might want to disable this behavior.
5 |
6 | Setting `disableAnalytics` to true will prevent any data from being sent.
7 | */
8 | "disableAnalytics": false
9 | }
10 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/addon-dynamic-demo/template.hbs:
--------------------------------------------------------------------------------
1 | {{!--BEGIN-SNIPPET addon-dynamic-demo.hbs--}}
2 |
8 | {{!--END-SNIPPET--}}
--------------------------------------------------------------------------------
/addon/components/ember-notifier-notification/close/template.hbs:
--------------------------------------------------------------------------------
1 | {{#if hasBlock}}
2 | {{yield}}
3 | {{else}}
4 |
12 | {{/if}}
--------------------------------------------------------------------------------
/config/addon-docs.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | 'use strict';
3 |
4 | const AddonDocsConfig = require('ember-cli-addon-docs/lib/config');
5 |
6 | module.exports = class extends AddonDocsConfig {
7 | // See https://ember-learn.github.io/ember-cli-addon-docs/docs/deploying
8 | // for details on configuration you can override here.
9 | };
10 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/animation/snippets/animation-config-environment.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET animation-config-environment.js
2 | let ENV = {
3 | emberNotifier: {
4 | showAnimationClass: 'my-animation-show-class',
5 | hideAnimationClass: 'my-animation-hide-class',
6 | animationTimeout: 150, // ms
7 | }
8 | }
9 | //! END-SNIPPET
10 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/styles/snippets/spectre-config-environment.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET spectre-config-environment.js
2 | let ENV = {
3 | emberNotifier: {
4 | 'primaryClass': 'toast-primary',
5 | 'successClass': 'toast-success',
6 | 'warningClass': 'toast-warning',
7 | 'dangerClass': 'toast-error',
8 | }
9 | }
10 | //! END-SNIPPET
11 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/styles/snippets/zurb-config-environment.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET zurb-config-environment.js
2 | let ENV = {
3 | emberNotifier: {
4 | 'primaryClass': 'primary',
5 | 'successClass': 'success',
6 | 'warningClass': 'warning',
7 | 'dangerClass': 'alert',
8 | 'secondaryClass': 'secondary',
9 | }
10 | }
11 | //! END-SNIPPET
12 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/task-message/template.hbs:
--------------------------------------------------------------------------------
1 | {{!--BEGIN-SNIPPET task-message.hbs--}}
2 |
3 | {{#if backgroundTask.isRunning}}
4 | Running background task...
5 | {{else}}
6 | {{#if backgroundTask.last.isSuccessful}}
7 | Task successful!
8 | {{else}}
9 | Task failed!
10 | {{/if}}
11 | {{/if}}
12 |
13 | {{!--END-SNIPPET--}}
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # unconventional js
2 | /blueprints/*/files/
3 | /vendor/
4 | tests/dummy/app/docs/**/snippets/**
5 |
6 | # compiled output
7 | /dist/
8 | /tmp/
9 |
10 | # dependencies
11 | /bower_components/
12 | /node_modules/
13 |
14 | # misc
15 | /coverage/
16 |
17 | # ember-try
18 | /.node_modules.ember-try/
19 | /bower.json.ember-try
20 | /package.json.ember-try
21 |
--------------------------------------------------------------------------------
/tests/dummy/app/styles/app.scss:
--------------------------------------------------------------------------------
1 | @import "ember-notifier";
2 |
3 | // Brand customization #282
4 | // https://github.com/ember-learn/ember-cli-addon-docs/issues/282
5 | .docs-font-title {
6 | font-family: serif;
7 | }
8 |
9 | .docs-bg-brand, body {
10 | background-color: #AA00AA;
11 | }
12 |
13 | .docs-md__a,
14 | .docs-text-brand {
15 | color: #9b4dca;
16 | }
17 |
--------------------------------------------------------------------------------
/addon/components/ember-notifier-notification/content/template.hbs:
--------------------------------------------------------------------------------
1 | {{#if contentComponent}}
2 | {{component
3 | contentComponent
4 | title=title
5 | message=message
6 | setOption=setOption}}
7 | {{else}}
8 |
9 | {{this.title}}
10 |
11 |
12 | {{this.message}}
13 |
14 | {{/if}}
--------------------------------------------------------------------------------
/tests/dummy/config/targets.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const browsers = [
4 | 'last 1 Chrome versions',
5 | 'last 1 Firefox versions',
6 | 'last 1 Safari versions'
7 | ];
8 |
9 | const isCI = !!process.env.CI;
10 | const isProduction = process.env.EMBER_ENV === 'production';
11 |
12 | if (isCI || isProduction) {
13 | browsers.push('ie 11');
14 | }
15 |
16 | module.exports = {
17 | browsers
18 | };
19 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/icons/snippets/fa-web-font-config-environment.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET fa-web-font-config-environment.js
2 | let ENV = {
3 | emberNotifier: {
4 | 'primaryIcon': 'fas fa-comment',
5 | 'infoIcon': 'fas fa-info',
6 | 'successIcon': 'fas fa-check',
7 | 'warningIcon': 'fas fa-exclamation',
8 | 'dangerIcon': 'fas fa-fire',
9 | 'secondaryIcon': 'fas fa-bell',
10 | }
11 | }
12 | //! END-SNIPPET
13 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/styles/snippets/bootstrap-config-environment.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET bootstrap-config-environment.js
2 | let ENV = {
3 | emberNotifier: {
4 | 'primaryClass': 'alert-primary',
5 | 'infoClass': 'alert-info',
6 | 'successClass': 'alert-success',
7 | 'warningClass': 'alert-warning',
8 | 'dangerClass': 'alert-danger',
9 | 'secondaryClass': 'alert-secondary',
10 | }
11 | }
12 | //! END-SNIPPET
13 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/icons/snippets/fa-svg-config-environment.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET fa-svg-config-environment.js
2 | let ENV = {
3 | emberNotifier: {
4 | 'primaryIcon': 'bell',
5 | 'infoIcon': 'info',
6 | 'successIcon': 'check',
7 | 'warningIcon': 'exclamation',
8 | 'dangerIcon': 'fire',
9 | 'secondaryIcon': 'comment'
10 | },
11 | fontawesome: {
12 | defaultPrefix: 'fas'
13 | }
14 | }
15 | //! END-SNIPPET
16 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/index/template.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | Install the package in your Ember project directory:
4 |
5 | {{#docs-snippet name="install.sh"}}
6 | ember install ember-notifier
7 | {{/docs-snippet}}
8 |
9 | This addon does not automatically include styling. You must import the
10 | addon style yourself.
11 |
12 | {{#docs-snippet name="app.css" title="styles/app.scss"}}
13 | @import "ember-notifier";
14 | {{/docs-snippet}}
15 |
--------------------------------------------------------------------------------
/tests/dummy/app/app.js:
--------------------------------------------------------------------------------
1 | import Application from '@ember/application';
2 | import Resolver from './resolver';
3 | import loadInitializers from 'ember-load-initializers';
4 | import config from './config/environment';
5 |
6 | const App = Application.extend({
7 | modulePrefix: config.modulePrefix,
8 | podModulePrefix: config.podModulePrefix,
9 | Resolver
10 | });
11 |
12 | loadInitializers(App, config.modulePrefix);
13 |
14 | export default App;
15 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 | end_of_line = lf
10 | charset = utf-8
11 | trim_trailing_whitespace = true
12 | insert_final_newline = true
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [*.hbs]
17 | insert_final_newline = false
18 |
19 | [*.{diff,md}]
20 | trim_trailing_whitespace = false
21 |
--------------------------------------------------------------------------------
/addon/components/ember-notifier-notification/template.hbs:
--------------------------------------------------------------------------------
1 | {{#if hasBlock}}
2 | {{yield}}
3 | {{else}}
4 | {{component
5 | iconComponent
6 | icon=notification.icon}}
7 | {{component
8 | contentComponent
9 | title=notification.title
10 | message=notification.message
11 | contentComponent=notification.contentComponent
12 | setOption=(action "setOption")}}
13 | {{component
14 | closeComponent
15 | close=close}}
16 | {{/if}}
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist/
3 | /tmp/
4 |
5 | # dependencies
6 | /bower_components/
7 | /node_modules/
8 |
9 | # misc
10 | /.sass-cache
11 | /connect.lock
12 | /coverage/
13 | /libpeerconnection.log
14 | /npm-debug.log*
15 | /testem.log
16 | /yarn-error.log
17 |
18 | # ember-try
19 | /.node_modules.ember-try/
20 | /bower.json.ember-try
21 | /package.json.ember-try
22 |
23 | # ember-cli-addon-docs
24 | /config/addon-docs.js
25 |
26 | # webstorm
27 | /.idea
28 |
--------------------------------------------------------------------------------
/tests/dummy/app/index/template.hbs:
--------------------------------------------------------------------------------
1 | {{docs-hero}}
2 |
3 |
4 | {{index/index-content}}
5 |
6 |
7 | {{#link-to
8 | "docs"
9 | class="docs-bg-grey-darkest hover:docs-bg-black docs-text-white docs-py-2 docs-px-4 docs-no-underline docs-rounded"}}
10 | Read the docs →
11 | {{/link-to}}
12 |
13 |
14 |
--------------------------------------------------------------------------------
/tests/unit/mixins/swipe-support-test.js:
--------------------------------------------------------------------------------
1 | import EmberObject from '@ember/object';
2 | import SwipeSupportMixin from 'ember-notifier/mixins/swipe-support';
3 | import { module, test } from 'qunit';
4 |
5 | module('Unit | Mixin | swipe-support', function() {
6 | // Replace this with your real tests.
7 | test('it works', function (assert) {
8 | let SwipeSupportObject = EmberObject.extend(SwipeSupportMixin);
9 | let subject = SwipeSupportObject.create();
10 | assert.ok(subject);
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/addon-dynamic-demo/component.js:
--------------------------------------------------------------------------------
1 | // BEGIN-SNIPPET addon-dynamic-demo.js
2 | import Component from '@ember/component';
3 | import { inject as service } from '@ember/service';
4 | import layout from './template';
5 |
6 | export default Component.extend({
7 | layout,
8 |
9 | notifier: service(),
10 |
11 | actions: {
12 | launchTask() {
13 | this.get('notifier').add({
14 | type: 'is-info',
15 | contentComponent: 'task-message',
16 | duration: 0,
17 | });
18 | },
19 | }
20 | });
21 | // END-SNIPPET
22 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist/
3 | /tmp/
4 |
5 | # dependencies
6 | /bower_components/
7 |
8 | # misc
9 | /.bowerrc
10 | /.editorconfig
11 | /.ember-cli
12 | /.eslintignore
13 | /.eslintrc.js
14 | /.gitignore
15 | /.watchmanconfig
16 | /.travis.yml
17 | /bower.json
18 | /config/ember-try.js
19 | /ember-cli-build.js
20 | /testem.js
21 | /tests/
22 | /yarn.lock
23 | .gitkeep
24 |
25 | # ember-try
26 | /.node_modules.ember-try/
27 | /bower.json.ember-try
28 | /package.json.ember-try
29 |
30 | # ember-cli-addon-docs
31 | /config/addon-docs.js
32 |
33 | # webstorm
34 | /.idea
35 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/styles/snippets/bulma-application.hbs:
--------------------------------------------------------------------------------
1 | {{! BEGIN-SNIPPET bulma-application.hbs }}
2 | {{#ember-notifier
3 | notificationClass="notification"
4 | as |notification close|}}
5 |
6 | {{#if notification.contentComponent}}
7 | {{component
8 | notification.contentComponent
9 | title=notification.title
10 | message=notification.message
11 | setOption=(action "setOption")}}
12 | {{else}}
13 | {{notification.message}}
14 | {{/if}}
15 | {{/ember-notifier}}
16 | {{outlet}}
17 | {{! END-SNIPPET }}
--------------------------------------------------------------------------------
/tests/dummy/app/docs/styles/snippets/spectre-application.hbs:
--------------------------------------------------------------------------------
1 | {{! BEGIN-SNIPPET spectre-application.hbs }}
2 | {{#ember-notifier
3 | notificationClass="toast"
4 | as |notification close|}}
5 |
6 |
7 | {{notification.title}}
8 |
9 | {{#if notification.contentComponent}}
10 | {{component
11 | notification.contentComponent
12 | title=notification.title
13 | message=notification.message
14 | setOption=(action "setOption")}}
15 | {{else}}
16 | {{notification.message}}
17 | {{/if}}
18 | {{/ember-notifier}}
19 | {{outlet}}
20 | {{! END-SNIPPET }}
--------------------------------------------------------------------------------
/tests/dummy/public/crossdomain.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/template.hbs:
--------------------------------------------------------------------------------
1 | {{#docs-viewer as |viewer|}}
2 | {{#viewer.nav as |nav|}}
3 | {{nav.section "Getting started"}}
4 | {{nav.item "Installation" "docs.index"}}
5 | {{nav.item "Usage" "docs.usage"}}
6 | {{nav.item "Demo" "docs.demo"}}
7 |
8 | {{nav.section "Customization"}}
9 | {{nav.item "Styles" "docs.styles"}}
10 | {{nav.item "Icons" "docs.icons"}}
11 | {{nav.item "Animation" "docs.animation"}}
12 | {{nav.item "Touch" "docs.touch"}}
13 | {{nav.item "Dynamic" "docs.dynamic"}}
14 | {{nav.item "Options" "docs.options"}}
15 | {{/viewer.nav}}
16 |
17 | {{#viewer.main}}
18 | {{outlet}}
19 | {{/viewer.main}}
20 | {{/docs-viewer}}
--------------------------------------------------------------------------------
/testem.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | test_page: 'tests/index.html?hidepassed',
3 | disable_watching: true,
4 | launch_in_ci: [
5 | 'Chrome',
6 | ],
7 | launch_in_dev: [
8 | 'Chrome'
9 | ],
10 | browser_args: {
11 | Chrome: {
12 | ci: [
13 | // --no-sandbox is needed when running Chrome inside a container
14 | process.env.CI ? '--no-sandbox' : null,
15 | '--headless',
16 | '--disable-gpu',
17 | '--disable-dev-shm-usage',
18 | '--disable-software-rasterizer',
19 | '--mute-audio',
20 | '--remote-debugging-port=0',
21 | '--window-size=1440,900'
22 | ].filter(Boolean)
23 | }
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/tailwind-alert/component.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import layout from './template';
3 |
4 | export default Component.extend({
5 | layout,
6 |
7 | classNames: ['docs-border-l-4', 'docs-p-4'],
8 | classNameBindings: [
9 | 'isInfo:docs-bg-purple-lightest',
10 | 'isInfo:docs-border-purple',
11 | 'isInfo:docs-text-purple-dark',
12 | 'isWarning:docs-bg-orange-lightest',
13 | 'isWarning:docs-border-orange',
14 | 'isWarning:docs-text-orange-dark',
15 | 'isDanger:docs-bg-red-lightest',
16 | 'isDanger:docs-border-red',
17 | 'isDanger:docs-text-red-dark',
18 | ],
19 | attributeBindings: ['role'],
20 |
21 | role: 'alert',
22 | });
23 |
--------------------------------------------------------------------------------
/addon/utils/default-value.js:
--------------------------------------------------------------------------------
1 | import { computed } from '@ember/object';
2 |
3 | /**
4 | * Fallback to a default value if the property is undefined.
5 | *
6 | * Source:
7 | * [github.com/cibernox/ember-power-select](https://github.com/cibernox/ember-power-select/blob/master/addon/utils/computed-fallback-if-undefined.js)
8 | *
9 | * @function defaultValue
10 | * @param {string} fallback Fallback value if property is undefined.
11 | * @return {string} The property value.
12 | */
13 | export default function defaultValue(fallback) {
14 | return computed({
15 | get() {
16 | return fallback;
17 | },
18 | set(_, value) {
19 | return value === undefined ? fallback : value;
20 | }
21 | });
22 | }
23 |
--------------------------------------------------------------------------------
/tests/dummy/app/router.js:
--------------------------------------------------------------------------------
1 | import AddonDocsRouter, { docsRoute } from 'ember-cli-addon-docs/router';
2 | import config from './config/environment';
3 |
4 | const Router = AddonDocsRouter.extend({
5 | location: config.locationType,
6 | rootURL: config.rootURL,
7 | });
8 |
9 | Router.map(function () {
10 | docsRoute(this, function () {
11 | // do not need to define index (installation)
12 | this.route('usage');
13 | this.route('demo');
14 |
15 | this.route('styles');
16 | this.route('icons');
17 | this.route('animation');
18 | this.route('touch');
19 | this.route('dynamic');
20 | this.route('options');
21 | });
22 |
23 | this.route('not-found', { path: '/*path' });
24 | });
25 |
26 | export default Router;
27 |
--------------------------------------------------------------------------------
/addon/components/ember-notifier-notification/icon/component.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import layout from './template';
3 |
4 | /**
5 | * The icon component.
6 | *
7 | * Inline usage:
8 | * ```hbs
9 | * {{ember-notifier-notification/icon icon=notification.icon}}
10 | * ```
11 | *
12 | * Block param usage:
13 | * ```hbs
14 | * {{#ember-notifier-notification/icon}}
15 | * {{fa-icon notification.icon}}
16 | * {{/ember-notifier-notification/icon}}
17 | * ```
18 | *
19 | * @class Icon
20 | */
21 | export default Component.extend({
22 | layout,
23 |
24 | classNames: ['ember-notifier-icon'],
25 |
26 | /**
27 | * Icon class name or object name.
28 | *
29 | * @argument icon
30 | * @type string
31 | */
32 | icon: null,
33 | });
34 |
--------------------------------------------------------------------------------
/tests/unit/utils/default-value-test.js:
--------------------------------------------------------------------------------
1 | import EmberObject from '@ember/object';
2 | import defaultValue from 'dummy/utils/default-value';
3 | import { module, test } from 'qunit';
4 |
5 | const Notification = EmberObject.extend({
6 | icon: defaultValue('bell'),
7 | });
8 |
9 | module('Unit | Utility | default-value', function (/* hooks */) {
10 | test('it uses the fallback value', function (assert) {
11 | assert.expect(1);
12 |
13 | const primary = Notification.create({});
14 |
15 | assert.equal(primary.get('icon'), 'bell');
16 | });
17 |
18 | test('it uses the set value', function (assert) {
19 | assert.expect(1);
20 |
21 | const danger = Notification.create({
22 | icon: 'fire',
23 | });
24 |
25 | assert.equal(danger.get('icon'), 'fire');
26 | });
27 | });
28 |
--------------------------------------------------------------------------------
/addon/components/ember-notifier-notification/close/component.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import layout from './template';
3 |
4 | /**
5 | * The close button component.
6 | *
7 | * Inline usage:
8 | * ```hbs
9 | * {{ember-notifier-notification/close close=close}}
10 | * ```
11 | *
12 | * Block param usage:
13 | * ```hbs
14 | * {{#ember-notifier-notification/close}}
15 | *
16 | * {{/ember-notifier-notification/close}}
17 | * ```
18 | *
19 | * @class Close
20 | */
21 | export default Component.extend({
22 | layout,
23 |
24 | classNames: ['ember-notifier-close'],
25 |
26 | /**
27 | * Closure action to dismiss the notification.
28 | *
29 | * @argument close
30 | * @type ember/action
31 | */
32 | close: null,
33 | });
34 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/options/snippets/notification-options.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET notification-options.js
2 | import Component from '@ember/component';
3 | import { inject as service } from '@ember/service';
4 |
5 | export default Component.extend({
6 | router: service(),
7 | notifier: service(),
8 |
9 | actions: {
10 | unauthenticated() {
11 | const router = this.get('router');
12 | this.get('notifier').warning(
13 | 'You must be logged in to access this resource ', {
14 | title: 'Unauthenticated',
15 | type: 'is-auth-error',
16 | icon: 'fas fa-user-astronaut',
17 | duration: 3200,
18 | onRemove() {
19 | router.transitionTo('login');
20 | },
21 | }
22 | );
23 | },
24 | }
25 | });
26 | //! END-SNIPPET
27 |
--------------------------------------------------------------------------------
/ember-cli-build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
4 | const sass = require('sass');
5 |
6 | module.exports = function(defaults) {
7 | let app = new EmberAddon(defaults, {
8 | sassOptions: {
9 | implementation: sass,
10 | },
11 | // Workaround: Broccoli Builder ran into an error with `UglifyWriter` plugin.
12 | 'ember-cli-uglify': {
13 | uglify: {
14 | compress: false,
15 | }
16 | },
17 | });
18 |
19 | /*
20 | This build file specifies the options for the dummy test app of this
21 | addon, located in `/tests/dummy`
22 | This build file does *not* influence how the addon or the app using it
23 | behave. You most likely want to be modifying `./index.js` or app's build file
24 | */
25 |
26 | return app.toTree();
27 | };
28 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/styles/snippets/zurb-application.hbs:
--------------------------------------------------------------------------------
1 | {{! BEGIN-SNIPPET zurb-application.hbs }}
2 | {{#ember-notifier
3 | notificationClass="callout"
4 | as |notification close|}}
5 |
6 | {{notification.title}}
7 |
8 |
9 | {{#if notification.contentComponent}}
10 | {{component
11 | notification.contentComponent
12 | title=notification.title
13 | message=notification.message
14 | setOption=(action "setOption")}}
15 | {{else}}
16 | {{notification.message}}
17 | {{/if}}
18 |
19 |
28 | {{/ember-notifier}}
29 | {{outlet}}
30 | {{! END-SNIPPET }}
--------------------------------------------------------------------------------
/tests/dummy/app/docs/styles/snippets/bootstrap-application.hbs:
--------------------------------------------------------------------------------
1 | {{! BEGIN-SNIPPET bootstrap-application.hbs }}
2 | {{#ember-notifier
3 | notificationClass="alert alert-dismissible"
4 | as |notification close|}}
5 |
6 | {{notification.title}}
7 |
8 |
9 | {{#if notification.contentComponent}}
10 | {{component
11 | notification.contentComponent
12 | title=notification.title
13 | message=notification.message
14 | setOption=(action "setOption")}}
15 | {{else}}
16 | {{notification.message}}
17 | {{/if}}
18 |
19 |
27 | {{/ember-notifier}}
28 | {{outlet}}
29 | {{! END-SNIPPET }}
--------------------------------------------------------------------------------
/addon/components/ember-notifier/template.hbs:
--------------------------------------------------------------------------------
1 | {{#each notifications as |notification|}}
2 | {{#if hasBlock}}
3 | {{#ember-notifier-notification
4 | class=(readonly notificationClass)
5 | notification=notification
6 | close=(action "remove" notification)
7 | iconComponent=(readonly iconComponent)
8 | contentComponent=(readonly contentComponent)
9 | closeComponent=(readonly closeComponent)}}
10 | {{yield notification (action "remove" notification)}}
11 | {{/ember-notifier-notification}}
12 | {{else}}
13 | {{ember-notifier-notification
14 | class=(readonly notificationClass)
15 | notification=notification
16 | close=(action "remove" notification)
17 | iconComponent=(readonly iconComponent)
18 | contentComponent=(readonly contentComponent)
19 | closeComponent=(readonly closeComponent)}}
20 | {{/if}}
21 | {{/each}}
--------------------------------------------------------------------------------
/tests/helpers/trigger-swipe-event.js:
--------------------------------------------------------------------------------
1 | import { triggerEvent } from '@ember/test-helpers';
2 |
3 | // Source: https://github.com/offirgolan/ember-burger-menu/blob/master/tests/helpers/trigger-swipe-event.js
4 | export default async function triggerSwipeEvent(el, direction, distance = 150) {
5 | let swipeRight = direction === 'right';
6 | let startPos = swipeRight ? 0 : distance;
7 | let endPos = swipeRight ? distance : 0;
8 |
9 | await triggerEvent(el, 'touchstart', {
10 | touches: [
11 | {
12 | pageX: startPos,
13 | pageY: 0,
14 | screenX: startPos,
15 | screenY: 0
16 | }
17 | ]
18 | });
19 |
20 | await triggerEvent(el, 'touchmove', {
21 | touches: [
22 | {
23 | pageX: endPos,
24 | pageY: 0,
25 | screenX: endPos,
26 | screenY: 0
27 | }
28 | ]
29 | });
30 |
31 | await triggerEvent(el, 'touchend');
32 | }
33 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/options/template.md:
--------------------------------------------------------------------------------
1 | # Options
2 |
3 | Notification options can be set globally and overridden when launching.
4 |
5 | ## Global
6 |
7 | In `config/environment.js`, the service defaults can be overridden using
8 | the `emberNotifier` object:
9 |
10 | {{docs-snippet name="global-options.js" title="config/environment.js"}}
11 |
12 | The notification container `position` can be set to:
13 |
14 | * `is-top`
15 | * `is-top-left`
16 | * `is-top-right`
17 | * `is-bottom`
18 | * `is-bottom-left`
19 | * `is-bottom-right`
20 |
21 | ## Notification
22 |
23 | The global config options can be overridden per notification. Extra
24 | options such as `onRemove()` callback function and `contentComponent`
25 | can be included. See {{#link-to "docs.api.item" "services/notifier" class="docs-md__a"}}ember-notifier.add(){{/link-to}}
26 | for all notification options.
27 |
28 | {{docs-snippet name="notification-options.js" title="component.js"}}
29 |
--------------------------------------------------------------------------------
/tests/integration/components/ember-notifier-notification/icon/component-test.js:
--------------------------------------------------------------------------------
1 | import { module, test } from 'qunit';
2 | import { setupRenderingTest } from 'ember-qunit';
3 | import { render } from '@ember/test-helpers';
4 | import hbs from 'htmlbars-inline-precompile';
5 |
6 | module('Integration | Component | ember-notifier-notification/icon', function(hooks) {
7 | setupRenderingTest(hooks);
8 |
9 | test('it renders', async function(assert) {
10 | assert.expect(4);
11 |
12 | await render(hbs`{{ember-notifier-notification/icon icon="fa-bell"}}`);
13 |
14 | assert.dom('.ember-notifier-icon').exists();
15 | assert.dom('.fa-bell').exists();
16 |
17 | await render(hbs`
18 | {{#ember-notifier-notification/icon}}
19 |
20 | {{/ember-notifier-notification/icon}}
21 | `);
22 |
23 | assert.dom('.ember-notifier-icon').exists();
24 | assert.dom('.fa-bell').exists();
25 | });
26 | });
27 |
--------------------------------------------------------------------------------
/tests/dummy/app/index/index-content/template.md:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |

8 |
9 |
10 |
11 | Features:
12 |
13 | * Uses **flexbox** and can be easily **customized using SASS/SCSS**.
14 | * Does not **impose or include** any frameworks or icon packages.
15 | * Easily **apply a framework's style** like Bootstrap's Alert.
16 | * Support for **dynamic components**, for example, a background task
17 | preparing a document to download.
18 |
19 | Inspiration:
20 |
21 | * [ember-cli-flash](https://github.com/poteto/ember-cli-flash)
22 | * [ember-cli-notifications](https://github.com/stonecircle/ember-cli-notifications)
23 | * [ember-notify](https://github.com/aexmachina/ember-notify)
24 | * [ember-notifyme](https://github.com/IgorKvasn/ember-notifyme)
25 |
--------------------------------------------------------------------------------
/config/deploy.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node */
2 | 'use strict';
3 |
4 | module.exports = function(deployTarget) {
5 | let ENV = {
6 | build: {}
7 | // include other plugin configuration that applies to all deploy targets here
8 | };
9 |
10 | if (deployTarget === 'development') {
11 | ENV.build.environment = 'development';
12 | // configure other plugins for development deploy target here
13 | }
14 |
15 | if (deployTarget === 'staging') {
16 | ENV.build.environment = 'production';
17 | // configure other plugins for staging deploy target here
18 | }
19 |
20 | if (deployTarget === 'production') {
21 | ENV.build.environment = 'production';
22 | // configure other plugins for production deploy target here
23 | }
24 |
25 | // Note: if you need to build some configuration asynchronously, you can return
26 | // a promise that resolves with the ENV object instead of returning the
27 | // ENV object synchronously.
28 | return ENV;
29 | };
30 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/options/snippets/global-options.js:
--------------------------------------------------------------------------------
1 | //! BEGIN-SNIPPET global-options.js
2 | module.exports = function (environment) {
3 | let ENV = {
4 | emberNotifier: {
5 | position: 'is-top-right',
6 | duration: 4200, // ms
7 | primaryClass: 'is-primary',
8 | primaryIcon: 'fas fa-bell',
9 | infoClass: 'is-info',
10 | infoIcon: 'fas fa-info',
11 | successClass: 'is-success',
12 | successIcon: 'fas fa-check',
13 | warningClass: 'is-warning',
14 | warningIcon: 'fas fa-exclamation',
15 | dangerClass: 'is-danger',
16 | dangerIcon: 'fas fa-fire',
17 | secondaryClass: 'is-secondary',
18 | secondaryIcon: 'fas fa-comment',
19 | showAnimationClass: 'ember-notifier-notification-show',
20 | hideAnimationClass: 'ember-notifier-notification-hide',
21 | animationTimeout: 500, // ms
22 | swipeDirection: 'right', // 'left' or 'right'
23 | minSwipeDistance: 150, // px
24 | maxSwipeTime: 300, // ms
25 | },
26 | };
27 |
28 | return ENV;
29 | };
30 | //! END-SNIPPET
31 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/touch/template.md:
--------------------------------------------------------------------------------
1 | # Touch
2 |
3 | A notification can be closed by swiping right on a touch device.
4 |
5 | ## Customize
6 |
7 | The following options are supported:
8 |
9 | * `swipeDirection` -- The horizontal direction the user must
10 | swipe. Can be `left`, `right`, or `null`. Defaults to `right`.
11 |
12 | * `minSwipeDistance` -- The minimum distance (px) the user must
13 | swipe. Defaults to `150`.
14 |
15 | * `maxSwipeTime` -- The maximum amount of time (ms) it must take the
16 | user to swipe. Defaults to `300`.
17 |
18 | Each option can be set using the global config or when launching a
19 | notification.
20 |
21 | {{docs-snippet name='touch-config-environment.js' title='config/environment.js'}}
22 |
23 | {{#docs-snippet name="touch-primary-function.js"}}
24 | this.get('notifier').primary('Primary notification', {
25 | contentComponent: 'my-large-dynamic-component',
26 | minSwipeDistance: '420',
27 | });
28 | {{/docs-snippet}}
29 |
30 | ## Disable
31 |
32 | Set `swipeDirection` to `null` to disable closing a notification by a
33 | swipe event.
34 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Dummy Tests
7 |
8 |
9 |
10 | {{content-for "head"}}
11 | {{content-for "test-head"}}
12 |
13 |
14 |
15 |
16 |
17 | {{content-for "head-footer"}}
18 | {{content-for "test-head-footer"}}
19 |
20 |
21 | {{content-for "body"}}
22 | {{content-for "test-body"}}
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | {{content-for "body-footer"}}
31 | {{content-for "test-body-footer"}}
32 |
33 |
34 |
--------------------------------------------------------------------------------
/tests/integration/components/ember-notifier-notification/close/component-test.js:
--------------------------------------------------------------------------------
1 | import { module, test } from 'qunit';
2 | import { setupRenderingTest } from 'ember-qunit';
3 | import { render, click } from '@ember/test-helpers';
4 | import hbs from 'htmlbars-inline-precompile';
5 |
6 | module('Integration | Component | ember-notifier-notification/close', function(hooks) {
7 | setupRenderingTest(hooks);
8 |
9 | test('it renders', async function(assert) {
10 | assert.expect(6);
11 |
12 | this.set('closeAction', () => assert.equal(true, true));
13 |
14 | await render(hbs`{{ember-notifier-notification/close close=(action closeAction)}}`);
15 |
16 | assert.dom('.ember-notifier-close').exists();
17 | assert.dom('.ember-notifier-close').hasText('×');
18 |
19 | await click('.ember-notifier-close-button');
20 |
21 | await render(hbs`
22 | {{#ember-notifier-notification/close}}
23 | my button
24 | {{/ember-notifier-notification/close}}
25 | `);
26 |
27 | assert.dom('.ember-notifier-close').exists();
28 | assert.dom('.ember-notifier-close').hasText('my button');
29 |
30 | await click('span');
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parserOptions: {
4 | ecmaVersion: 2017,
5 | sourceType: 'module'
6 | },
7 | plugins: [
8 | 'ember'
9 | ],
10 | extends: [
11 | 'eslint:recommended',
12 | 'plugin:ember/recommended'
13 | ],
14 | env: {
15 | browser: true
16 | },
17 | rules: {
18 | },
19 | overrides: [
20 | // node files
21 | {
22 | files: [
23 | '.eslintrc.js',
24 | '.template-lintrc.js',
25 | 'ember-cli-build.js',
26 | 'index.js',
27 | 'testem.js',
28 | 'blueprints/*/index.js',
29 | 'config/**/*.js',
30 | 'tests/dummy/config/**/*.js'
31 | ],
32 | excludedFiles: [
33 | 'addon/**',
34 | 'addon-test-support/**',
35 | 'app/**',
36 | 'tests/dummy/app/**'
37 | ],
38 | parserOptions: {
39 | sourceType: 'script',
40 | ecmaVersion: 2015
41 | },
42 | env: {
43 | browser: false,
44 | node: true
45 | },
46 | plugins: ['node'],
47 | rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, {
48 | // add your custom rules and overrides for node files here
49 | })
50 | }
51 | ]
52 | };
53 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/animation/template.md:
--------------------------------------------------------------------------------
1 | # Animation
2 |
3 | CSS transitions are used to animate the launching and closing of a
4 | notification. Check out [animista](http://animista.net/) for various
5 | CSS transitions.
6 |
7 | ## Customize
8 |
9 | The following options are supported:
10 |
11 | * `showAnimationClass` -- The transition class applied when a
12 | notification is launched. Defaults to
13 | `ember-notifier-notification-show`.
14 |
15 | * `hideAnimationClass` -- The transition class applied when a
16 | notification is closed. Defaults to `ember-notifier-notification-hide`.
17 |
18 | * `animationTimeout` -- The number of milliseconds before a
19 | notification is closed. It should equal the CSS `animation-delay`
20 | property value. Defaults to `500`.
21 |
22 | Each option can be set using the global config or when launching a
23 | notification.
24 |
25 | {{docs-snippet name='animation-config-environment.js' title='config/environment.js'}}
26 |
27 | {{#docs-snippet name="animation-primary-function.js"}}
28 | this.get('notifier').primary('Primary notification', {
29 | showAnimationClass: 'my-animation-show-class',
30 | });
31 | {{/docs-snippet}}
32 |
33 | ## Disable
34 |
35 | Set `showAnimationClass` and `hideAnimationClass` classes to `null`
36 | and `animationTimeout` to `0`.
37 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | ---
2 | language: node_js
3 | node_js:
4 | # we recommend testing addons with the same minimum supported node version as Ember CLI
5 | # so that your addon works for all apps
6 | - "6"
7 |
8 | sudo: false
9 | dist: trusty
10 |
11 | addons:
12 | chrome: stable
13 |
14 | cache:
15 | directories:
16 | - $HOME/.npm
17 | - $HOME/.cache/yarn
18 |
19 | env:
20 | global:
21 | # See https://git.io/vdao3 for details.
22 | - JOBS=1
23 |
24 | jobs:
25 | fail_fast: true
26 | allow_failures:
27 | - env: EMBER_TRY_SCENARIO=ember-canary
28 |
29 | include:
30 | # runs linting and tests with current locked deps
31 |
32 | - stage: "Tests"
33 | name: "Tests"
34 | script:
35 | - npm run lint:hbs
36 | - npm run lint:js
37 | - npm test
38 |
39 | # we recommend new addons test the current and previous LTS
40 | # as well as latest stable release (bonus points to beta/canary)
41 | - stage: "Additional Tests"
42 | env: EMBER_TRY_SCENARIO=ember-lts-2.16
43 | - env: EMBER_TRY_SCENARIO=ember-lts-2.18
44 | - env: EMBER_TRY_SCENARIO=ember-release
45 | - env: EMBER_TRY_SCENARIO=ember-beta
46 | - env: EMBER_TRY_SCENARIO=ember-canary
47 | - env: EMBER_TRY_SCENARIO=ember-default-with-jquery
48 |
49 | before_install:
50 | - npm config set spin false
51 | - npm install -g npm@4
52 | - npm --version
53 |
54 | script:
55 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO
56 |
--------------------------------------------------------------------------------
/addon/components/ember-notifier-notification/content/component.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import layout from './template';
3 |
4 | /**
5 | * The content component which displays the title and message.
6 | *
7 | * Inline usage:
8 | * ```hbs
9 | * {{ember-notifier-notification/content
10 | * title=notification.title
11 | * message=notification.message}}
12 | * ```
13 | *
14 | * Passing an a component name to `contentComponent` will rendered with the following
15 | * arguments:
16 | * ```hbs
17 | * {{component
18 | * contentComponent
19 | * title=title
20 | * message=message
21 | * setOption=setOption}}
22 | * ```
23 | *
24 | * @class Content
25 | */
26 | export default Component.extend({
27 | layout,
28 |
29 | classNames: ['ember-notifier-content'],
30 |
31 | /**
32 | * Notification title/header.
33 | *
34 | * @argument title
35 | * @type [string]
36 | */
37 | title: null,
38 |
39 | /**
40 | * Notification message.
41 | *
42 | * @argument message
43 | * @type [string]
44 | */
45 | message: null,
46 |
47 | /**
48 | * Content component.
49 | *
50 | * @argument contentComponent
51 | * @type [Component]
52 | */
53 | contentComponent: null,
54 |
55 | /**
56 | * Closure action to change a property on the notification object.
57 | *
58 | * @argument setOption
59 | * @type ember/action
60 | */
61 | setOption: null,
62 | });
63 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/icons/template.md:
--------------------------------------------------------------------------------
1 | # Icons
2 |
3 | Ember Notifier does not ship with an icon framework. If you are using a
4 | web font, you can take advantage of the global config icon class
5 | options. For SVG icons, it is recommended to create a custom icon
6 | component.
7 |
8 | ## Web Font
9 |
10 | {{#tailwind-alert isInfo=true}}
11 | Example using [Font Awesome 5 Web Fonts with CSS](https://fontawesome.com/get-started/web-fonts-with-css).
12 | {{/tailwind-alert}}
13 |
14 | Define each global config option icon class.
15 |
16 | {{docs-snippet name='fa-web-font-config-environment.js' title='config/environment.js'}}
17 |
18 | ## SVG
19 |
20 | {{#tailwind-alert isInfo=true}}
21 | Example using [ember-fontawesome](https://github.com/FortAwesome/ember-fontawesome).
22 | {{/tailwind-alert}}
23 |
24 | Create an icon component.
25 |
26 | {{#docs-snippet name="generate-notification-icon.sh"}}
27 | ember g component notification-icon
28 | {{/docs-snippet}}
29 |
30 | {{#docs-demo as |demo|}}
31 | {{demo.snippet 'fa-svg-component.js' label='component.js'}}
32 | {{demo.snippet 'fa-svg-component.hbs' label='template.hbs'}}
33 | {{/docs-demo}}
34 |
35 | Define icon names in the global options config.
36 |
37 | {{docs-snippet name='fa-svg-config-environment.js' title='config/environment.js'}}
38 |
39 | Set `notification-icon` for `iconComponent` argument.
40 |
41 | {{docs-snippet name='fa-svg-application.hbs' title='templates/application.hbs'}}
42 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/dynamic/template.md:
--------------------------------------------------------------------------------
1 | # Dynamic
2 |
3 | The notification's content can be made dynamic by passing a component
4 | name to the `contentComponent` option.
5 |
6 | {{#docs-snippet name="message-component-option.js"}}
7 | this.get('notifier').info(null, { contentComponent: 'my-component' });
8 | {{/docs-snippet}}
9 |
10 | For example: We are going to create a background task
11 | using [ember-concurrency](http://ember-concurrency.com/). Based on the
12 | task's result, the notification will be updated using the content
13 | component closure action `setOption`. If the task fails, it will be
14 | toggled to `is-danger` or if successful, `is-success`.
15 |
16 | Install `ember-concurrency`.
17 |
18 | {{#docs-snippet name="install-ember-concurrency"}}
19 | ember install ember-concurrency
20 | {{/docs-snippet}}
21 |
22 | Generate the dynamic content component.
23 |
24 | {{#docs-snippet name="ember-generate-notification-icon"}}
25 | ember g component task-message
26 | {{/docs-snippet}}
27 |
28 | {{#docs-demo as |demo|}}
29 | {{demo.snippet 'task-message.js' label='component.js'}}
30 | {{demo.snippet 'task-message.hbs' label='template.hbs'}}
31 | {{/docs-demo}}
32 |
33 | {{#docs-demo as |demo|}}
34 | {{#demo.example name='addon-dynamic-demo-component.hbs'}}
35 | {{addon-dynamic-demo}}
36 | {{/demo.example}}
37 |
38 | {{demo.snippet 'addon-dynamic-demo.js' label='component.js'}}
39 | {{demo.snippet 'addon-dynamic-demo.hbs' label='template.hbs'}}
40 | {{/docs-demo}}
41 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/usage/template.md:
--------------------------------------------------------------------------------
1 | # Usage
2 |
3 | Add the `ember-notifier` component to your application template. This
4 | container displays notifications.
5 |
6 | {{#docs-snippet name="usage-template.hbs" title="templates/application.hbs"}}
7 | {{ember-notifier position="is-top-right"}}
8 | {{outlet}}
9 | {{/docs-snippet}}
10 |
11 | Inject the `notifier` service anywhere you want to launch a
12 | notification.
13 |
14 | {{#docs-snippet name="usage-service.js"}}
15 | import Component from '@ember/component';
16 | import { inject as service } from '@ember/service';
17 |
18 | export default Component.extend({
19 | notifier: service(),
20 | });
21 | {{/docs-snippet}}
22 |
23 | Notifications can be launched by calling styled functions.
24 |
25 | {{#docs-snippet name="usage-styled-functions.js"}}
26 | this.get('notifier').primary('Primary notification');
27 | this.get('notifier').info('Information notification');
28 | this.get('notifier').success('Success notification');
29 | this.get('notifier').warning('Warning notification');
30 | this.get('notifier').danger('Danger notification');
31 | this.get('notifier').secondary('Secondary notification');
32 | {{/docs-snippet}}
33 |
34 | Custom notifications can be launched using `add()`.
35 |
36 | {{#docs-snippet name="usage-add-function.js"}}
37 | this.get('notifier').add('Custom notification', { type: 'custom-class' });
38 | {{/docs-snippet}}
39 |
40 | All notifications can be closed using `empty()`.
41 |
42 | {{#docs-snippet name="empty.js"}}
43 | this.get('notifier').empty();
44 | {{/docs-snippet}}
45 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/task-message/component.js:
--------------------------------------------------------------------------------
1 | // BEGIN-SNIPPET task-message.js
2 | import Component from '@ember/component';
3 | import { on } from '@ember/object/evented';
4 | import { task, timeout } from 'ember-concurrency';
5 | import layout from './template';
6 |
7 | export default Component.extend({
8 | layout,
9 |
10 | didInsertElement() {
11 | this._super(...arguments);
12 | this.get('backgroundTask').perform();
13 | },
14 |
15 | didDestroyElement() {
16 | this.get('backgroundTask').cancelAll();
17 | this._super(...arguments);
18 | },
19 |
20 | backgroundTask: task(function* () {
21 | const ms = 2000 + 2000 * Math.random();
22 | yield timeout(ms);
23 |
24 | if (parseInt(ms) % 2 === 0) {
25 | throw new Error('Background task failed.');
26 | }
27 | return {};
28 | }).evented(),
29 |
30 | // eslint-disable-next-line ember/no-on-calls-in-components
31 | backgroundTaskStarted: on('backgroundTask:started', function (/* taskInstance */) {
32 | this.setOption('icon', 'fas fa-spinner fa-pulse');
33 | }),
34 |
35 | // eslint-disable-next-line ember/no-on-calls-in-components
36 | backgroundTaskSucceeded: on('backgroundTask:succeeded', function (/* taskInstance */) {
37 | this.setOption('icon', 'fas fa-check');
38 | this.setOption('type', 'is-success');
39 | }),
40 |
41 | // eslint-disable-next-line ember/no-on-calls-in-components
42 | backgroundTaskErrored: on('backgroundTask:errored', function (/* taskInstance, error */) {
43 | this.setOption('icon', 'fas fa-fire');
44 | this.setOption('type', 'is-danger');
45 | }),
46 | });
47 | // END-SNIPPET
48 |
--------------------------------------------------------------------------------
/tests/integration/components/ember-notifier-notification/content/component-test.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import { module, test } from 'qunit';
3 | import { setupRenderingTest } from 'ember-qunit';
4 | import { render } from '@ember/test-helpers';
5 | import hbs from 'htmlbars-inline-precompile';
6 |
7 | module('Integration | Component | ember-notifier-notification/content', function(hooks) {
8 | setupRenderingTest(hooks);
9 |
10 | test('it renders', async function(assert) {
11 | assert.expect(5);
12 |
13 | this.set('message', 'msg');
14 | this.set('title', 'header');
15 |
16 | await render(hbs`{{ember-notifier-notification/content message=message title=title}}`);
17 |
18 | assert.dom('.ember-notifier-content').exists();
19 | assert.dom('.ember-notifier-title').exists();
20 | assert.dom('.ember-notifier-title').hasText(this.get('title'));
21 | assert.dom('.ember-notifier-message').exists();
22 | assert.dom('.ember-notifier-message').hasText(this.get('message'));
23 | });
24 |
25 | test('it renders a custom content component', async function(assert) {
26 | assert.expect(4);
27 |
28 | this.set('message', 'msg');
29 | this.set('title', 'header');
30 |
31 | this.owner.register('component:content-component', Component.extend({
32 | layout: hbs`{{message}}
`,
33 | }));
34 |
35 | await render(hbs`{{ember-notifier-notification/content contentComponent="content-component" message=message title=title}}`);
36 |
37 | assert.dom('header').exists();
38 | assert.dom('header').hasText(this.get('title'));
39 | assert.dom('p').exists();
40 | assert.dom('p').hasText(this.get('message'));
41 | });
42 | });
43 |
44 |
45 |
--------------------------------------------------------------------------------
/addon/mixins/swipe-support.js:
--------------------------------------------------------------------------------
1 | import { readOnly } from '@ember/object/computed';
2 | import Mixin from '@ember/object/mixin';
3 | import { isNone } from '@ember/utils';
4 |
5 | let meta;
6 |
7 | // Source: https://github.com/offirgolan/ember-burger-menu/blob/master/addon/mixins/swipe-support.js
8 | export default Mixin.create({
9 | minSwipeDistance: readOnly('notification.minSwipeDistance'),
10 | maxSwipeTime: readOnly('notification.maxSwipeTime'),
11 |
12 | onSwipe(/* direction, target */) {
13 | },
14 |
15 | touchStart(event) {
16 | this._super(...arguments);
17 |
18 | const touch = event.touches[0];
19 |
20 | meta = {
21 | target: event.target,
22 | start: {
23 | x: touch.pageX,
24 | y: touch.pageY,
25 | time: new Date().getTime(),
26 | }
27 | };
28 | },
29 |
30 | touchMove(event) {
31 | this._super(...arguments);
32 |
33 | const touch = event.touches[0];
34 |
35 | meta.differences = {
36 | x: touch.pageX - meta.start.x,
37 | y: touch.pageY - meta.start.y
38 | };
39 |
40 | // Compute swipe direction
41 | if (isNone(meta.isHorizontal)) {
42 | meta.isHorizontal = Math.abs(meta.differences.x) > Math.abs(meta.differences.y);
43 | }
44 |
45 | // A valid swipe event uses only one finger
46 | if (event.touches.length > 1) {
47 | meta.isInvalid = true;
48 | }
49 | },
50 |
51 | touchEnd() {
52 | this._super(...arguments);
53 |
54 | const minSwipeDistance = this.get('minSwipeDistance');
55 | const maxSwipeTime = this.get('maxSwipeTime');
56 | const elapsedTime = new Date().getTime() - meta.start.time;
57 |
58 | if (meta.isHorizontal
59 | && !meta.isInvalid
60 | && Math.abs(meta.differences.x) >= minSwipeDistance
61 | && elapsedTime <= maxSwipeTime) {
62 | this.onSwipe(meta.differences.x > 0 ? 'right' : 'left', meta.target);
63 | }
64 | }
65 | });
66 |
--------------------------------------------------------------------------------
/tests/dummy/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Ember Notifier Documentation
7 |
8 |
9 |
10 | {{content-for "head"}}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {{content-for "head-footer"}}
26 |
27 |
28 | {{content-for "body"}}
29 |
30 |
31 |
32 |
33 | {{content-for "body-footer"}}
34 |
35 |
36 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [v0.1.3](https://github.com/scottwernervt/ember-notifier/releases/tag/v0.1.3) (2018-12-11)
4 |
5 | Bugs:
6 |
7 | - Leaflet defines an `{{icon}}` helper which gets called instead of rendering the `{{icon}}` property value in the icon component template. [\#21](https://github.com/scottwernervt/ember-notifier/issues/21#issuecomment-444194637)
8 | - Add extra styling to close button to make it independent of frameworks. [\#21](https://github.com/scottwernervt/ember-notifier/issues/21#issuecomment-444144615)
9 |
10 | | Old | New |
11 | | ------------------------------------- | -------------------------------------------- |
12 | | `$ember-notifier-close-width` | `$ember-notifier-close-button-width` |
13 | | | `$ember-notifier-close-button-height` |
14 | | `$ember-notifier-close-color` | `$ember-notifier-close-button-color` |
15 | | `$ember-notifier-close-opacity` | `$ember-notifier-close-button-opacity` |
16 | | `$ember-notifier-close-hover-opacity` | `$ember-notifier-close-button-hover-opacity` |
17 |
18 | Maintenance:
19 |
20 | - Update dependencies and dev dependencies.
21 |
22 | Docs:
23 |
24 | - Add custom theme to docs.
25 |
26 | ## [v0.1.2](https://github.com/scottwernervt/ember-notifier/releases/tag/v0.1.2) (2018-11-08)
27 |
28 | Maintenance:
29 |
30 | - Update dependencies and dev dependencies.
31 |
32 | ## [v0.1.1](https://github.com/scottwernervt/ember-notifier/releases/tag/v0.1.1) (2018-09-27)
33 |
34 | Features:
35 |
36 | - Swipe right to remove a notification. [\#4](https://github.com/scottwernervt/ember-notifier/issues/4)
37 | - Add CSS transitions when adding and removing a notification. [\#3](https://github.com/scottwernervt/ember-notifier/issues/3)
38 |
39 | Maintenance:
40 |
41 | - Update dependencies and dev dependencies.
42 |
43 | ## [v0.1.0](https://github.com/scottwernervt/ember-notifier/releases/tag/v0.1.0) (2018-09-27)
44 |
45 | Depreciated due to issue with deploying with `ember-cli-addon-docs`.
46 |
47 | ## [v0.0.1](https://github.com/scottwernervt/ember-notifier/releases/tag/v0.0.1) (2018-07-30)
48 |
49 | - First release.
50 |
--------------------------------------------------------------------------------
/config/ember-try.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const getChannelURL = require('ember-source-channel-url');
4 |
5 | module.exports = function() {
6 | return Promise.all([
7 | getChannelURL('release'),
8 | getChannelURL('beta'),
9 | getChannelURL('canary')
10 | ]).then((urls) => {
11 | return {
12 | // TODO: Toggle to false (https://github.com/ember-cli/ember-try/issues/207).
13 | useYarn: true,
14 | scenarios: [
15 | {
16 | name: 'ember-lts-2.16',
17 | env: {
18 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true })
19 | },
20 | npm: {
21 | devDependencies: {
22 | '@ember/jquery': '^0.5.1',
23 | 'ember-source': '~2.16.0'
24 | }
25 | }
26 | },
27 | {
28 | name: 'ember-lts-2.18',
29 | env: {
30 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true })
31 | },
32 | npm: {
33 | devDependencies: {
34 | '@ember/jquery': '^0.5.1',
35 | 'ember-source': '~2.18.0'
36 | }
37 | }
38 | },
39 | {
40 | name: 'ember-release',
41 | npm: {
42 | devDependencies: {
43 | 'ember-source': urls[0]
44 | }
45 | }
46 | },
47 | {
48 | name: 'ember-beta',
49 | npm: {
50 | devDependencies: {
51 | 'ember-source': urls[1]
52 | }
53 | }
54 | },
55 | {
56 | name: 'ember-canary',
57 | npm: {
58 | devDependencies: {
59 | 'ember-source': urls[2]
60 | }
61 | }
62 | },
63 | {
64 | name: 'ember-default',
65 | npm: {
66 | devDependencies: {}
67 | }
68 | },
69 | {
70 | name: 'ember-default-with-jquery',
71 | env: {
72 | EMBER_OPTIONAL_FEATURES: JSON.stringify({
73 | 'jquery-integration': true
74 | })
75 | },
76 | npm: {
77 | devDependencies: {
78 | '@ember/jquery': '^0.5.1'
79 | }
80 | }
81 | }
82 | ]
83 | };
84 | });
85 | };
86 |
--------------------------------------------------------------------------------
/tests/dummy/app/docs/styles/template.md:
--------------------------------------------------------------------------------
1 | # Styles
2 |
3 | This addon supports customizing the notification style through SASS or
4 | using `ember-notifier` as a block param to apply a framework's
5 | component class.
6 |
7 | ## SASS
8 |
9 | To customize the notification, set the addon variables before
10 | importing `ember-notifier.scss`.
11 |
12 | {{docs-snippet name='style-sass.scss' title='styles/app.scss' language='css'}}
13 |
14 | This approach is powerful enough to build entire themes on top of it.
15 | The entire list of available variables can be found in
16 | [ember-notifier.scss](https://github.com/scottwernervt/ember-notifier/blob/master/app/styles/ember-notifier.scss).
17 |
18 | ## Frameworks
19 |
20 | Applying a framework's theme is accomplished by mapping the addon's
21 | global config options to the framework's notification classes and using
22 | `ember-notifier` as a block param.
23 |
24 | ### Bulma
25 |
26 | {{#docs-demo as |demo|}}
27 | {{demo.snippet 'bulma-config-environment.js' label='environment.js'}}
28 | {{demo.snippet 'bulma-application.hbs' label='application.hbs'}}
29 | {{/docs-demo}}
30 |
31 | Documentation on [Bulma Notifications](https://bulma.io/documentation/elements/notification/).
32 |
33 | ### Spectre
34 |
35 | {{#docs-demo as |demo|}}
36 | {{demo.snippet 'spectre-config-environment.js' label='environment.js'}}
37 | {{demo.snippet 'spectre-application.hbs' label='application.hbs'}}
38 | {{/docs-demo}}
39 |
40 | Documentation on [Spectre Toasts](https://picturepan2.github.io/spectre/components.html#toasts).
41 |
42 | ### Bootstrap
43 |
44 | {{#docs-demo as |demo|}}
45 | {{demo.snippet 'bootstrap-config-environment.js' label='environment.js'}}
46 | {{demo.snippet 'bootstrap-application.hbs' label='application.hbs'}}
47 | {{/docs-demo}}
48 |
49 | Documentation on [Bootstrap Alerts](https://getbootstrap.com/docs/4.1/components/alerts/).
50 |
51 | ### Zurb Foundation
52 |
53 | {{#docs-demo as |demo|}}
54 | {{demo.snippet 'zurb-config-environment.js' label='environment.js'}}
55 | {{demo.snippet 'zurb-application.hbs' label='application.hbs'}}
56 | {{/docs-demo}}
57 |
58 | Documentation on [Zurb Foundation Callouts](https://foundation.zurb.com/sites/docs/callout.html).
59 |
60 | ### Other
61 |
62 | Do not see your framework? Make a request [here](https://github.com/scottwernervt/ember-notifier/issues).
63 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/addon-demo/component.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import { alias } from '@ember/object/computed';
3 | import { inject as service } from '@ember/service';
4 | import { dasherize } from '@ember/string';
5 | import layout from './template';
6 |
7 | export default Component.extend({
8 | layout,
9 |
10 | router: service(),
11 | notifier: service(),
12 | demo: service(),
13 |
14 | message: 'Testing notifications',
15 | contentComponent: null,
16 | types: null,
17 | positions: null,
18 |
19 | type: alias('demo.type'),
20 | position: alias('demo.position'),
21 | duration: alias('demo.duration'),
22 |
23 | init() {
24 | this._super(...arguments);
25 | this.set('types', ['primary', 'info', 'success', 'warning', 'danger', 'secondary']);
26 | this.set('positions', ['top', 'top left', 'top right', 'bottom', 'bottom left', 'bottom right']);
27 | },
28 |
29 | actions: {
30 | show() {
31 | const type = this.get('type');
32 | const message = this.get('message');
33 | const duration = this.get('duration');
34 | const contentComponent = this.get('contentComponent');
35 | const title = `Notification ${type}`; // framework testing
36 | const options = { title, duration, contentComponent };
37 |
38 | if (type === 'primary') {
39 | this.get('notifier').primary(message, options);
40 | } else if (type === 'info') {
41 | this.get('notifier').info(message, options);
42 | } else if (type === 'success') {
43 | this.get('notifier').success(message, options);
44 | } else if (type === 'warning') {
45 | this.get('notifier').warning(message, options);
46 | } else if (type === 'danger') {
47 | this.get('notifier').danger(message, options);
48 | } else if (type === 'secondary') {
49 | this.get('notifier').secondary(message, options);
50 | }
51 | },
52 |
53 | unauthenticated() {
54 | const router = this.get('router');
55 | this.get('notifier').warning(
56 | 'You must be logged in to access this resource ', {
57 | title: 'Unauthenticated',
58 | type: 'is-auth-error',
59 | icon: 'fas fa-user-astronaut',
60 | duration: 3200,
61 | onRemove() {
62 | router.transitionTo('docs.index');
63 | },
64 | }
65 | );
66 | },
67 |
68 | setPosition(position) {
69 | this.set('demo.position', dasherize(`is-${position}`));
70 | },
71 |
72 | clearAll() {
73 | this.get('notifier').empty();
74 | },
75 | }
76 | });
77 |
--------------------------------------------------------------------------------
/addon/components/ember-notifier/component.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import { readOnly } from '@ember/object/computed';
3 | import { inject as service } from '@ember/service';
4 | import layout from './template';
5 |
6 | /**
7 | * The notification container which positions and displays active notifications.
8 | *
9 | * Inline usage:
10 | * ```hbs
11 | * {{ember-notifier position="is-top-right"}}
12 | * ```
13 | *
14 | * Block param usage:
15 | * ```hbs
16 | * {{#ember-notifier notificationClass='alert' as |notification close|}}
17 | * {{notification.title}}
18 | * {{notification.message}}
19 | *
20 | * {{/ember-notifier}}
21 | * ```
22 | *
23 | * @class EmberNotifier
24 | * @yield {EmberObject} notification The notification object.
25 | * @yield {ember/action} close Closure action to dismiss the notification.
26 | */
27 | export default Component.extend({
28 | layout,
29 |
30 | notifier: service(),
31 |
32 | classNames: ['ember-notifier'],
33 | classNameBindings: ['position'],
34 |
35 | /**
36 | * The default class name for notifications.
37 | *
38 | * @argument notificationClass
39 | * @type string
40 | */
41 | notificationClass: 'ember-notifier-notification',
42 |
43 | /**
44 | * The location class name of notifications on the screen.
45 | *
46 | * Supported positions:
47 | * * `is-top`
48 | * * `is-top-left`
49 | * * `is-top-right`
50 | * * `is-bottom`
51 | * * `is-bottom-left`
52 | * * `is-bottom-right`
53 | *
54 | * @argument position='is-top-right'
55 | * @type [string]
56 | */
57 | position: null,
58 |
59 | /**
60 | * The default icon component.
61 | *
62 | * @argument iconComponent
63 | * @type [string]
64 | */
65 | iconComponent: undefined,
66 |
67 | /**
68 | * The default content component.
69 | *
70 | * @argument contentComponent
71 | * @type [string]
72 | */
73 | contentComponent: undefined,
74 |
75 | /**
76 | * The default close component.
77 | *
78 | * @argument closeComponent
79 | * @type [string]
80 | */
81 | closeComponent: undefined,
82 |
83 | notifications: readOnly('notifier.notifications'),
84 |
85 | init() {
86 | this._super(...arguments);
87 | const config = this.get('notifier.config');
88 | if (!this.get('position')) {
89 | this.set('position', config.position);
90 | }
91 | },
92 |
93 | willDestroyElement() {
94 | this._super(...arguments);
95 | this.get('notifier').empty();
96 | },
97 |
98 | actions: {
99 | remove(message) {
100 | this.get('notifier').remove(message);
101 | },
102 | },
103 | });
104 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ember-notifier",
3 | "version": "0.1.3",
4 | "description": "Easy, beautiful notifications for your Ember project.",
5 | "keywords": [
6 | "ember-addon",
7 | "ember",
8 | "ember-cli",
9 | "notification",
10 | "notify",
11 | "flash",
12 | "callout",
13 | "growl",
14 | "toast"
15 | ],
16 | "repository": {
17 | "type": "git",
18 | "url": "https://github.com/scottwernervt/ember-notifier.git"
19 | },
20 | "license": "MIT",
21 | "author": {
22 | "name": "Scott Werner",
23 | "email": "scott.werner.vt@gmail.com",
24 | "url": "https://github.com/scottwernervt"
25 | },
26 | "directories": {
27 | "doc": "doc",
28 | "test": "tests"
29 | },
30 | "homepage": "https://scottwernervt.github.io/ember-notifier",
31 | "bugs": {
32 | "url": "https://github.com/scottwernervt/ember-notifier/issues",
33 | "email": "scott.werner.vt@gmail.com"
34 | },
35 | "scripts": {
36 | "build": "ember build",
37 | "lint:hbs": "ember-template-lint .",
38 | "lint:js": "eslint .",
39 | "start": "ember serve",
40 | "test": "ember test",
41 | "test:all": "ember try:each",
42 | "test:reset": "ember try:reset",
43 | "test:server": "ember test --server"
44 | },
45 | "dependencies": {
46 | "ember-cli-babel": "7.1.4",
47 | "ember-cli-htmlbars": "^3.0.1"
48 | },
49 | "devDependencies": {
50 | "@ember/optional-features": "^0.7.0",
51 | "broccoli-asset-rev": "^3.0.0",
52 | "ember-ajax": "^4.0.1",
53 | "ember-cli": "~3.5.1",
54 | "ember-cli-addon-docs": "^0.6.1",
55 | "ember-cli-addon-docs-yuidoc": "^0.2.1",
56 | "ember-cli-dependency-checker": "^3.0.0",
57 | "ember-cli-deploy": "^1.0.2",
58 | "ember-cli-deploy-build": "^1.1.1",
59 | "ember-cli-deploy-git": "^1.3.3",
60 | "ember-cli-deploy-git-ci": "^1.0.1",
61 | "ember-cli-eslint": "^5.0.0",
62 | "ember-cli-htmlbars-inline-precompile": "^2.0.0",
63 | "ember-cli-inject-live-reload": "^2.0.1",
64 | "ember-cli-qunit": "^4.4.0",
65 | "ember-cli-sass": "^8.0.1",
66 | "ember-cli-sri": "^2.1.1",
67 | "ember-cli-template-lint": "^1.0.0-beta.2",
68 | "ember-cli-uglify": "^2.1.0",
69 | "ember-concurrency": "^0.8.25",
70 | "ember-disable-prototype-extensions": "^1.1.3",
71 | "ember-export-application-global": "^2.0.0",
72 | "ember-load-initializers": "^2.0.0",
73 | "ember-maybe-import-regenerator": "^0.1.6",
74 | "ember-resolver": "^5.0.1",
75 | "ember-source": "~3.6.0",
76 | "ember-source-channel-url": "^1.1.0",
77 | "ember-try": "^1.1.0",
78 | "eslint-plugin-ember": "^6.0.1",
79 | "eslint-plugin-node": "^8.0.0",
80 | "loader.js": "^4.7.0",
81 | "qunit-dom": "^0.8.3",
82 | "sass": "^1.15.2"
83 | },
84 | "engines": {
85 | "node": "6.* || 8.* || >= 10.*"
86 | },
87 | "ember-addon": {
88 | "configPath": "tests/dummy/config"
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/tests/dummy/app/application/template.hbs:
--------------------------------------------------------------------------------
1 | {{docs-header}}
2 |
3 | {{outlet}}
4 |
5 | {{docs-keyboard-shortcuts}}
6 |
7 | {{!--addon inline --}}
8 | {{ember-notifier position=demo.position}}
9 |
10 | {{!--addon block param --}}
11 | {{!--{{#ember-notifier notificationClass='alert' as |notification close|}}--}}
12 | {{!--{{notification.title}}
--}}
13 | {{!--{{notification.message}}
--}}
14 | {{!----}}
15 | {{!--{{/ember-notifier}}--}}
16 |
17 | {{!--bulma--}}
18 | {{!--{{#ember-notifier notificationClass='notification' as |notification close|}}--}}
19 | {{!----}}
20 | {{!--{{#if notification.contentComponent}}--}}
21 | {{!--{{component--}}
22 | {{!--notification.contentComponent--}}
23 | {{!--title=notification.title--}}
24 | {{!--message=notification.message}}--}}
25 | {{!--{{else}}--}}
26 | {{!--{{notification.message}}--}}
27 | {{!--{{/if}}--}}
28 | {{!--{{/ember-notifier}}--}}
29 |
30 | {{!--spectre--}}
31 | {{!--{{#ember-notifier notificationClass="toast" as |notification close|}}--}}
32 | {{!----}}
33 | {{!--{{notification.title}}
--}}
34 | {{!--{{#if notification.contentComponent}}--}}
35 | {{!--{{component--}}
36 | {{!--notification.contentComponent--}}
37 | {{!--title=notification.title--}}
38 | {{!--message=notification.message}}--}}
39 | {{!--{{else}}--}}
40 | {{!--{{notification.message}}--}}
41 | {{!--{{/if}}--}}
42 | {{!--{{/ember-notifier}}--}}
43 |
44 | {{!--foundation--}}
45 | {{!--{{#ember-notifier notificationClass="callout" as |notification close|}}--}}
46 | {{!--{{notification.title}}
--}}
47 | {{!----}}
48 | {{!--{{#if notification.contentComponent}}--}}
49 | {{!--{{component--}}
50 | {{!--notification.contentComponent--}}
51 | {{!--title=notification.title--}}
52 | {{!--message=notification.message}}--}}
53 | {{!--{{else}}--}}
54 | {{!--{{notification.message}}--}}
55 | {{!--{{/if}}--}}
56 | {{!--
--}}
57 | {{!----}}
60 | {{!--{{/ember-notifier}}--}}
61 |
62 | {{!--bootstrap--}}
63 | {{!--{{#ember-notifier notificationClass="alert alert-dismissible" as |notification close|}}--}}
64 | {{!--{{notification.title}}
--}}
65 | {{!----}}
66 | {{!--{{#if notification.contentComponent}}--}}
67 | {{!--{{component--}}
68 | {{!--notification.contentComponent--}}
69 | {{!--title=notification.title--}}
70 | {{!--message=notification.message}}--}}
71 | {{!--{{else}}--}}
72 | {{!--{{notification.message}}--}}
73 | {{!--{{/if}}--}}
74 | {{!--
--}}
75 | {{!----}}
78 | {{!--{{/ember-notifier}}--}}
79 |
--------------------------------------------------------------------------------
/tests/dummy/config/environment.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(environment) {
4 | let ENV = {
5 | modulePrefix: 'dummy',
6 | environment,
7 | rootURL: '/',
8 | locationType: 'auto',
9 | EmberENV: {
10 | FEATURES: {
11 | // Here you can enable experimental features on an ember canary build
12 | // e.g. 'with-controller': true
13 | },
14 | EXTEND_PROTOTYPES: {
15 | // Prevent Ember Data from overriding Date.parse.
16 | Date: false
17 | }
18 | },
19 |
20 | emberNotifier: {
21 | 'position': 'is-top',
22 | // showAnimationClass: null,
23 | // hideAnimationClass: null,
24 | // animationTimeout: 0, // ms
25 | //
26 | // addon
27 | //
28 | // 'primaryClass': 'is-primary',
29 | // 'infoClass': 'is-info',
30 | // 'successClass': 'is-success',
31 | // 'warningClass': 'is-warning',
32 | // 'dangerClass': 'is-danger',
33 | // 'secondaryClass': 'is-secondary',
34 | //
35 | // bulma
36 | //
37 | // 'secondaryClass': 'is-link',
38 | //
39 | // spectre
40 | //
41 | // 'primaryClass': 'toast-primary',
42 | // 'infoClass': '',
43 | // 'successClass': 'toast-success',
44 | // 'warningClass': 'toast-warning',
45 | // 'dangerClass': 'toast-error',
46 | //
47 | // zurb foundation
48 | //
49 | // 'primaryClass': 'primary',
50 | // 'successClass': 'success',
51 | // 'warningClass': 'warning',
52 | // 'dangerClass': 'alert',
53 | // 'secondaryClass': 'secondary',
54 | //
55 | // bootstrap
56 | //
57 | // 'primaryClass': 'alert-primary',
58 | // 'infoClass': 'alert-info',
59 | // 'successClass': 'alert-success',
60 | // 'warningClass': 'alert-warning',
61 | // 'dangerClass': 'alert-danger',
62 | // 'secondaryClass': 'alert-secondary',
63 | //
64 | // font awesome web font
65 | //
66 | // 'primaryIcon': 'fas fa-comment',
67 | // 'infoIcon': 'fas fa-info',
68 | // 'successIcon': 'fas fa-check',
69 | // 'warningIcon': 'fas fa-exclamation',
70 | // 'dangerIcon': 'fas fa-fire',
71 | // 'secondaryIcon': 'fas fa-bell',
72 | //
73 | // font awesome svg icon
74 | //
75 | // 'primaryIcon': 'bell',
76 | // 'infoIcon': 'info',
77 | // 'successIcon': 'check',
78 | // 'warningIcon': 'exclamation',
79 | // 'dangerIcon': 'fire',
80 | // 'secondaryIcon': 'comment',
81 | },
82 |
83 | APP: {
84 | // Here you can pass flags/options to your application instance
85 | // when it is created
86 | }
87 | };
88 |
89 | if (environment === 'development') {
90 | // ENV.APP.LOG_RESOLVER = true;
91 | // ENV.APP.LOG_ACTIVE_GENERATION = true;
92 | // ENV.APP.LOG_TRANSITIONS = true;
93 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
94 | // ENV.APP.LOG_VIEW_LOOKUPS = true;
95 | }
96 |
97 | if (environment === 'test') {
98 | // Testem prefers this...
99 | ENV.locationType = 'none';
100 |
101 | // keep test console output quieter
102 | ENV.APP.LOG_ACTIVE_GENERATION = false;
103 | ENV.APP.LOG_VIEW_LOOKUPS = false;
104 |
105 | ENV.APP.rootElement = '#ember-testing';
106 | ENV.APP.autoboot = false;
107 | }
108 |
109 | if (environment === 'production') {
110 | // Allow ember-cli-addon-docs to update the rootURL in compiled assets
111 | ENV.rootURL = 'ADDON_DOCS_ROOT_URL';
112 | // here you can enable a production-specific feature
113 | }
114 |
115 | return ENV;
116 | };
117 |
--------------------------------------------------------------------------------
/tests/integration/components/ember-notifier/component-test.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import { click, render } from '@ember/test-helpers';
3 | import { setupRenderingTest } from 'ember-qunit';
4 | import hbs from 'htmlbars-inline-precompile';
5 | import { module, test } from 'qunit';
6 |
7 | function setupService(hooks) {
8 | hooks.afterEach(function (/* assert */) {
9 | this.owner.lookup('service:notifier').empty();
10 | });
11 | }
12 |
13 | module('Integration | Component | ember-notifier', function (hooks) {
14 | setupRenderingTest(hooks);
15 | setupService(hooks);
16 |
17 | test('it renders', async function (assert) {
18 | assert.expect(3);
19 |
20 | await render(hbs`{{ember-notifier}}`);
21 |
22 | assert.dom('.ember-notifier').exists();
23 | assert.dom('.ember-notifier').hasClass('is-top');
24 |
25 | await render(hbs`{{ember-notifier position="is-bottom"}}`);
26 |
27 | assert.dom('.ember-notifier').hasClass('is-bottom');
28 | });
29 |
30 | test('it renders a notification', async function (assert) {
31 | assert.expect(5);
32 |
33 | const service = this.owner.lookup('service:notifier');
34 | service.add({ title: 'header', message: 'msg', duration: 0 });
35 |
36 | await render(hbs`{{ember-notifier}}`);
37 |
38 | assert.dom('.ember-notifier-notification').exists();
39 | assert.dom('.ember-notifier-notification-base').exists();
40 | assert.dom('.ember-notifier-content').exists();
41 | assert.dom('.ember-notifier-title').hasText('header');
42 | assert.dom('.ember-notifier-message').hasText('msg');
43 | });
44 |
45 | test('it renders a notification with custom class', async function (assert) {
46 | assert.expect(2);
47 |
48 | const service = this.owner.lookup('service:notifier');
49 | service.add({ title: 'header', message: 'msg', duration: 0 });
50 |
51 | await render(hbs`{{ember-notifier notificationClass="alert"}}`);
52 |
53 | assert.dom('.alert').exists();
54 | assert.dom('.ember-notifier-notification-base').exists();
55 | });
56 |
57 | test('a custom component can use the close closure action', async function (assert) {
58 | assert.expect(2);
59 |
60 | const service = this.owner.lookup('service:notifier');
61 | service.add({ title: 'header', message: 'msg', duration: 0 });
62 |
63 | await render(hbs`
64 | {{#ember-notifier as |notification close|}}
65 | {{notification.title}}
66 | {{notification.message}}
67 |
68 | {{/ember-notifier}}
69 | `);
70 | await click('button');
71 |
72 | assert.dom('.ember-notifier-notification').exists({ count: 0 });
73 | assert.equal(service.get('notifications.length'), 0, 'Notification was removed by button click.');
74 | });
75 |
76 | test('a custom component for icon, message, and close can be used', async function (assert) {
77 | assert.expect(7);
78 |
79 | this.owner.register('component:icon-component', Component.extend({
80 | layout: hbs`{{icon}}
`
81 | }));
82 | this.owner.register('component:message-component', Component.extend({
83 | layout: hbs`{{title}} - {{message}}
`
84 | }));
85 | this.owner.register('component:close-component', Component.extend({
86 | layout: hbs``
87 | }));
88 |
89 | const service = this.owner.lookup('service:notifier');
90 | service.add({ title: 'header', message: 'msg', icon: 'bell', duration: 0 });
91 |
92 | await render(hbs`
93 | {{ember-notifier
94 | iconComponent="icon-component"
95 | contentComponent="message-component"
96 | closeComponent="close-component"}}
97 | `);
98 |
99 | assert.dom('.icon-component').exists();
100 | assert.dom('.icon-component').hasText('bell');
101 | assert.dom('.message-component').exists();
102 | assert.dom('.message-component').hasText('header - msg');
103 | assert.dom('.close-component').exists();
104 |
105 | await click('.close-component');
106 |
107 | assert.dom('.ember-notifier-notification').exists({ count: 0 });
108 | assert.equal(service.get('notifications.length'), 0, 'Notification was removed by button click.');
109 | });
110 | });
111 |
--------------------------------------------------------------------------------
/tests/dummy/app/components/addon-demo/template.hbs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ember-notifier
2 | ==============
3 |
4 | [![Latest NPM release][npm-badge]][npm-badge-url]
5 | [![License][license-badge]][license-badge-url]
6 | [![TravisCI Build Status][travis-badge]][travis-badge-url]
7 | [![Ember Observer Score][ember-observer-badge]][ember-observer-badge-url]
8 | [![Code Climate][codeclimate-badge]][codeclimate-badge-url]
9 | [![Dependencies][dependencies-badge]][dependencies-badge-url]
10 | [![Dev Dependencies][devDependencies-badge]][devDependencies-badge-url]
11 |
12 | [npm-badge]: https://img.shields.io/npm/v/ember-notifier.svg
13 | [npm-badge-url]: https://www.npmjs.com/package/ember-notifier
14 | [travis-badge]: https://img.shields.io/travis/scottwernervt/ember-notifier/master.svg
15 | [travis-badge-url]: https://travis-ci.org/scottwernervt/ember-notifier
16 | [codeclimate-badge]: https://api.codeclimate.com/v1/badges/24b82ae0cd54584332e2/maintainability
17 | [codeclimate-badge-url]: https://codeclimate.com/github/scottwernervt/ember-notifier
18 | [ember-observer-badge]: http://emberobserver.com/badges/ember-notifier.svg
19 | [ember-observer-badge-url]: http://emberobserver.com/addons/ember-notifier
20 | [license-badge]: https://img.shields.io/npm/l/ember-notifier.svg
21 | [license-badge-url]: LICENSE.md
22 | [dependencies-badge]: https://david-dm.org/scottwernervt/ember-notifier.svg
23 | [dependencies-badge-url]: https://david-dm.org/scottwernervt/ember-notifier
24 | [devDependencies-badge]: https://david-dm.org/scottwernervt/ember-notifier/dev-status.svg?theme=shields.io
25 | [devDependencies-badge-url]: https://david-dm.org/scottwernervt/ember-notifier?type=dev
26 |
27 | Easy, beautiful notifications for your Ember project.
28 |
29 |
30 | Installation
31 | ------------------------------------------------------------------------------
32 |
33 | ```
34 | ember install ember-notifier
35 | ```
36 |
37 |
38 | Usage
39 | ------------------------------------------------------------------------------
40 |
41 | Add the `ember-notifier` component to your application template. This
42 | container displays notifications.
43 |
44 | ```hbs
45 | {{ember-notifier position="is-top-right"}}
46 | {{outlet}}
47 | ```
48 |
49 | Inject the `notifier` service anywhere you want to launch a
50 | notification.
51 |
52 | ```js
53 | import Component from '@ember/component';
54 | import { inject as service } from '@ember/service';
55 |
56 | export default Component.extend({
57 | notifier: service(),
58 | });
59 | ```
60 |
61 | Notifications can be added by calling styled functions.
62 |
63 | ```js
64 | this.get('notifier').primary('Primary notification');
65 | this.get('notifier').info('Information notification');
66 | this.get('notifier').success('Success notification');
67 | this.get('notifier').warning('Warning notification');
68 | this.get('notifier').danger('Danger notification');
69 | this.get('notifier').secondary('Secondary notification');
70 | ```
71 |
72 | Custom notifications can be called using `add()`.
73 |
74 | ```js
75 | this.get('notifier').add('Custom notification', { type: 'custom-class' });
76 | ```
77 |
78 | All notifications can be cleared using `empty()`.
79 |
80 | ```js
81 | this.get('notifier').empty();
82 | ```
83 |
84 | Check out the [documentation](https://scottwernervt.github.io/ember-notifier)
85 | for more customization and options.
86 |
87 | Contributing
88 | ------------------------------------------------------------------------------
89 |
90 | ### Issues
91 |
92 | Demonstrate bug using the provided [ember-twiddle](https://ember-twiddle.com/fc59431098da6a27cac671d0142a6440).
93 |
94 | ### Installation
95 |
96 | * `git clone https://github.com/scottwernervt/ember-notifier`
97 | * `cd ember-notifier`
98 | * `npm install`
99 |
100 | ### Linting
101 |
102 | * `npm run lint:hbs`
103 | * `npm run lint:js`
104 | * `npm run lint:js -- --fix`
105 |
106 | ### Running tests
107 |
108 | * `ember test` – Runs the test suite on the current Ember version
109 | * `ember test --server` – Runs the test suite in "watch mode"
110 | * `ember try:each` – Runs the test suite against multiple Ember versions
111 |
112 | ### Running the dummy application
113 |
114 | * `ember serve`
115 | * Visit the dummy application at [http://localhost:4200](http://localhost:4200).
116 |
117 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/).
118 |
119 | Favicon
120 | --------
121 |
122 | Created at [favicon.io](https://favicon.io/?t=EN&ff=Aldrich&fs=72&fc=%23FFF&b=rounded&bc=%23A0A).
123 |
124 | License
125 | ------------------------------------------------------------------------------
126 |
127 | This project is licensed under the [MIT License](LICENSE.md).
128 |
--------------------------------------------------------------------------------
/addon/components/ember-notifier-notification/component.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import { readOnly } from '@ember/object/computed';
3 | import { inject as service } from '@ember/service';
4 | import SwipeSupportMixin from 'ember-notifier/mixins/swipe-support';
5 | import defaultValue from 'ember-notifier/utils/default-value';
6 | import layout from './template';
7 |
8 | /**
9 | * The notification component which displays the icon, content, and close button.
10 | *
11 | * Inline usage:
12 | * ```hbs
13 | * {{ember-notifier-notification
14 | * notification=notification
15 | * close=(action "remove" notification)}}
16 | * ```
17 | *
18 | * Custom notification components:
19 | * ```hbs
20 | * {{ember-notifier-notification
21 | * notification=notification
22 | * close=(action "remove" notification)
23 | * iconComponent='my-icon-component'
24 | * contentComponent='my-content-component'
25 | * closeComponent='my-close-component'}}
26 | * ```
27 | *
28 | * If the mouse enters the notification and the notification is scheduled to be removed, it will be
29 | * paused and then restarted when the mouse leaves. If the user swipes right, the notification will
30 | * be closed.
31 | *
32 | * @class EmberNotifierNotification
33 | */
34 | export default Component.extend(SwipeSupportMixin, {
35 | layout,
36 |
37 | notifier: service(),
38 |
39 | classNames: ['ember-notifier-notification-base'],
40 | classNameBindings: ['typeClass', 'animationStateClass'],
41 | attributeBindings: ['role'],
42 | role: 'alert',
43 |
44 | /**
45 | * The notification options object.
46 | *
47 | * @argument notification
48 | * @type !Object
49 | */
50 | notification: undefined,
51 |
52 | /**
53 | * Closure action to remove the notification.
54 | *
55 | * @argument close
56 | * @type ember/action
57 | */
58 | close: null,
59 |
60 | /**
61 | * The notification type class name.
62 | *
63 | * @argument typeClass
64 | * @type string
65 | * @readOnly
66 | */
67 | typeClass: readOnly('notification.type'),
68 |
69 | /**
70 | * The notification animation state class name.
71 | *
72 | * @argument animationStateClass
73 | * @type string
74 | * @readOnly
75 | */
76 | animationStateClass: readOnly('notification.animationState'),
77 |
78 | /**
79 | * The number of pixels a user must move before notification will close.
80 | *
81 | * @argument minSwipeDistance
82 | * @type number
83 | * @readOnly
84 | */
85 | swipeThreshold: readOnly('notification.minSwipeDistance'),
86 |
87 | /**
88 | * The number of milliseconds a user must move before notification will close.
89 | *
90 | * @argument maxSwipeTime
91 | * @type number
92 | * @readOnly
93 | */
94 | swipeTimeout: readOnly('notification.maxSwipeTime'),
95 |
96 | /**
97 | * The swipe direction, "left" or "right", to close a notification.
98 | *
99 | * @argument swipeDirection
100 | * @type string
101 | * @readOnly
102 | */
103 | swipeDirection: readOnly('notification.swipeDirection'),
104 |
105 | /**
106 | * The icon component to render.
107 | *
108 | * Default is: `ember-notifier-notification/icon`.
109 | *
110 | * @argument iconComponent
111 | * @type string
112 | */
113 | iconComponent: defaultValue('ember-notifier-notification/icon'),
114 |
115 | /**
116 | * The content component to render.
117 | *
118 | * Default is: `ember-notifier-notification/content`.
119 | *
120 | * @argument contentComponent
121 | * @type string
122 | */
123 | contentComponent: defaultValue('ember-notifier-notification/content'),
124 |
125 | /**
126 | * The close component to render.
127 | *
128 | * Default is: `ember-notifier-notification/close`.
129 | *
130 | * @argument closeComponent
131 | * @type string
132 | */
133 | closeComponent: defaultValue('ember-notifier-notification/close'),
134 |
135 | willDestroyElement() {
136 | this._super(...arguments);
137 |
138 | const notification = this.get('notification');
139 | if (notification.get('timer')) {
140 | this.get('notifier').cancelRemoval(notification);
141 | }
142 | },
143 |
144 | actions: {
145 | setOption(property, value) {
146 | this.get('notification').set(property, value);
147 | }
148 | },
149 |
150 | mouseEnter(/* event */) {
151 | const notification = this.get('notification');
152 | if (notification.get('duration') > 0) {
153 | this.get('notifier').cancelRemoval(notification);
154 | }
155 | },
156 |
157 | mouseLeave(/* event */) {
158 | const notification = this.get('notification');
159 | if (notification.get('duration') > 0) {
160 | this.get('notifier').scheduleRemoval(notification);
161 | }
162 | },
163 |
164 | onSwipe(direction) {
165 | if (this.get('swipeDirection') === direction) {
166 | this.close();
167 | }
168 | }
169 | });
170 |
--------------------------------------------------------------------------------
/tests/unit/services/notifier-test.js:
--------------------------------------------------------------------------------
1 | import { run } from '@ember/runloop';
2 | import { settled } from '@ember/test-helpers'
3 | import { typeOf } from '@ember/utils';
4 | import { setupTest } from 'ember-qunit';
5 | import { module, test } from 'qunit';
6 |
7 | module('Unit | Service | notifier', function (hooks) {
8 | setupTest(hooks);
9 |
10 | test('#notification property returns an array of notifications', function (assert) {
11 | assert.expect(2);
12 |
13 | const service = this.owner.factoryFor('service:notifier').create({});
14 |
15 | service.info('info 1');
16 | service.info('info 2');
17 | service.info('info 3');
18 |
19 | assert.equal(typeOf(service.get('notifications')), 'array', 'it returns an array');
20 | assert.equal(service.get('notifications.length'), 3, 'it returns the correct number of notifications');
21 | });
22 |
23 | test('#add adds a custom notification', async function (assert) {
24 | assert.expect(7);
25 |
26 | const service = this.owner.factoryFor('service:notifier').create({});
27 | let result = false;
28 |
29 | service.add({
30 | message: 'msg',
31 | title: 'header',
32 | type: 'is-class',
33 | icon: 'bell',
34 | duration: 0,
35 | contentComponent: 'test-component',
36 | onRemove() {
37 | result = true;
38 | },
39 | });
40 |
41 | assert.equal(service.get('notifications.0.message'), 'msg', 'it has the correct message');
42 | assert.equal(service.get('notifications.0.title'), 'header', 'it has the correct title');
43 | assert.equal(service.get('notifications.0.type'), 'is-class', 'it has the correct type');
44 | assert.equal(service.get('notifications.0.icon'), 'bell', 'it has the correct icon');
45 | assert.equal(service.get('notifications.0.duration'), 0, 'it has the correct duration');
46 | assert.equal(service.get('notifications.0.contentComponent'), 'test-component', 'it has the correct content component');
47 |
48 | service.remove(service.get('notifications.0'));
49 |
50 | await settled();
51 |
52 | assert.equal(result, true, 'it called the onRemove function');
53 | });
54 |
55 | test('#remove removes a notification', async function (assert) {
56 | assert.expect(1);
57 |
58 | const service = this.owner.factoryFor('service:notifier').create({});
59 |
60 | service.primary('primary');
61 | service.remove(service.get('notifications.0'));
62 |
63 | await settled();
64 |
65 | assert.equal(service.get('notifications.length'), 0, 'it removes the notification');
66 | });
67 |
68 | test('#cancelRemoval cancels removal of a notification', function (assert) {
69 | assert.expect(1);
70 |
71 | const done = assert.async();
72 | const service = this.owner.factoryFor('service:notifier').create({});
73 |
74 | service.add({
75 | message: 'schedule',
76 | duration: 500,
77 | animationTimeout: 0,
78 | });
79 | service.cancelRemoval(service.get('notifications.0'));
80 |
81 | run.later(() => {
82 | assert.equal(service.get('notifications.length'), 1, 'it cancels the removal of the notification');
83 | done();
84 | }, 750);
85 | });
86 |
87 | test('#scheduleRemoval schedules removal of a notification', function (assert) {
88 | assert.expect(1);
89 |
90 | const done = assert.async();
91 | const service = this.owner.factoryFor('service:notifier').create({});
92 |
93 | service.add({
94 | message: 'schedule',
95 | duration: 500,
96 | animationTimeout: 0,
97 | });
98 |
99 | run.later(() => {
100 | assert.equal(service.get('notifications.length'), 0, 'it removes the notification automatically');
101 | done();
102 | }, 750);
103 | });
104 |
105 | test('#empty clears the notifications', async function (assert) {
106 | assert.expect(2);
107 |
108 | const service = this.owner.factoryFor('service:notifier').create({});
109 |
110 | service.info('info 1');
111 | service.info('info 2');
112 | service.info('info 3');
113 | service.empty();
114 |
115 | await settled();
116 |
117 | assert.equal(typeOf(service.get('notifications')), 'array', 'it returns an array');
118 | assert.equal(service.get('notifications.length'), 0, 'it empties the array');
119 | });
120 |
121 | test('it has default notification styles', function (assert) {
122 | assert.expect(12);
123 |
124 | const service = this.owner.factoryFor('service:notifier').create({});
125 | const defaultStyles = ['primary', 'info', 'success', 'warning', 'danger', 'secondary'];
126 |
127 | defaultStyles.forEach((style) => {
128 | const method = service[style];
129 |
130 | assert.ok(method);
131 | assert.equal(typeOf(method), 'function');
132 | });
133 | });
134 |
135 | test('it has default notification animations for adding and removing', async function (assert) {
136 | assert.expect(2);
137 |
138 | const service = this.owner.factoryFor('service:notifier').create({});
139 | service.add({ message: 'msg', duration: 0 });
140 |
141 | const notification = service.get('notifications').get('firstObject');
142 |
143 | assert.equal(notification.get('animationState'), 'ember-notifier-notification-show');
144 |
145 | service.remove(notification);
146 | await settled();
147 |
148 | assert.equal(notification.get('animationState'), 'ember-notifier-notification-hide');
149 | });
150 | });
151 |
--------------------------------------------------------------------------------
/tests/integration/components/ember-notifier-notification/component-test.js:
--------------------------------------------------------------------------------
1 | import Component from '@ember/component';
2 | import EmberObject from '@ember/object';
3 | import { click, find, render } from '@ember/test-helpers';
4 | import { setupRenderingTest } from 'ember-qunit';
5 | import hbs from 'htmlbars-inline-precompile';
6 | import { module, test } from 'qunit';
7 | import triggerSwipeEvent from '../../../helpers/trigger-swipe-event';
8 |
9 | const template = hbs`
10 | {{ember-notifier-notification notification=notification close=(action "onClose")}}
11 | `;
12 |
13 | module('Integration | Component | ember-notifier-notification', function (hooks) {
14 | setupRenderingTest(hooks);
15 |
16 | hooks.beforeEach(function () {
17 | this.notification = EmberObject.create({
18 | title: 'title',
19 | message: 'message',
20 | type: 'is-primary',
21 | icon: 'primary-icon',
22 | showAnimationClass: null,
23 | hideAnimationClass: null,
24 | animationTimeout: 0,
25 | minSwipeDistance: 150,
26 | maxSwipeTime: 300,
27 | swipeDirection: 'right',
28 | });
29 | this.actions = {
30 | onClose: () => void 0,
31 | };
32 | this.send = (actionName, ...args) =>
33 | this.actions[actionName].apply(this, args);
34 | });
35 |
36 | test('it renders', async function (assert) {
37 | assert.expect(10);
38 |
39 | await render(template);
40 |
41 | assert
42 | .dom('.ember-notifier-notification-base')
43 | .exists();
44 | assert
45 | .dom('.ember-notifier-notification-base')
46 | .hasClass('is-primary', 'Notification has correct type class');
47 | assert
48 | .dom('.ember-notifier-content')
49 | .exists();
50 | assert
51 | .dom('.ember-notifier-icon')
52 | .exists();
53 | assert
54 | .dom('.ember-notifier-icon > span > i')
55 | .hasClass('primary-icon', 'Notification has an primary icon class');
56 | assert
57 | .dom('.ember-notifier-title')
58 | .exists();
59 | assert
60 | .dom('.ember-notifier-title')
61 | .hasText('title', 'Notification has a title');
62 | assert
63 | .dom('.ember-notifier-message')
64 | .exists();
65 | assert
66 | .dom('.ember-notifier-message')
67 | .hasText('message', 'Notification has a message');
68 | assert
69 | .dom('.ember-notifier-close')
70 | .exists();
71 | });
72 |
73 | test('it renders as block param', async function (assert) {
74 | assert.expect(7);
75 |
76 | await render(hbs`
77 | {{#ember-notifier-notification
78 | notificationClass="alert"
79 | notification=notification
80 | close=(action "onClose")}}
81 | {{notification.title}}
82 | {{notification.message}}
83 |
84 | {{/ember-notifier-notification}}
85 | `);
86 |
87 | assert
88 | .dom('.ember-notifier-notification-base')
89 | .exists();
90 | assert
91 | .dom('.ember-notifier-notification-base')
92 | .hasClass('is-primary', 'Notification has correct type class');
93 | assert
94 | .dom('h5')
95 | .exists();
96 | assert
97 | .dom('h5')
98 | .hasText('title', 'Notification has a title');
99 | assert
100 | .dom('p')
101 | .hasText('message', 'Notification has a message');
102 | assert
103 | .dom('button')
104 | .exists();
105 | assert
106 | .dom('button')
107 | .hasText('Close', 'Close button has text');
108 | });
109 |
110 | test('it renders with custom components', async function (assert) {
111 | assert.expect(11);
112 |
113 | let onClose = false;
114 | this.actions.onClose = () => (onClose = true);
115 |
116 | this.owner.register('component:icon-component', Component.extend({
117 | layout: hbs`
118 |
119 | {{icon}}
120 |
`
121 | }));
122 |
123 | this.owner.register('component:content-component', Component.extend({
124 | layout: hbs`
125 |
126 |
127 | {{title}}
128 |
129 |
130 | {{message}}
131 |
132 |
`
133 | }));
134 |
135 | this.owner.register('component:close-component', Component.extend({
136 | layout: hbs`
137 | `
140 | }));
141 |
142 | await render(hbs`
143 | {{ember-notifier-notification
144 | iconComponent="icon-component"
145 | contentComponent="content-component"
146 | closeComponent="close-component"
147 | notification=notification
148 | close=(action "onClose")}}
149 | `);
150 |
151 | assert
152 | .dom('.ember-notifier-notification-base')
153 | .exists();
154 | assert
155 | .dom('.ember-notifier-notification-base')
156 | .hasClass('is-primary', 'Notification has correct type class');
157 | assert
158 | .dom('.icon-component')
159 | .exists();
160 | assert
161 | .dom('.icon-component')
162 | .hasText('primary-icon', 'Notification has an icon type');
163 | assert
164 | .dom('.content-component')
165 | .exists();
166 | assert
167 | .dom('.title')
168 | .exists();
169 | assert
170 | .dom('.title')
171 | .hasText('title', 'Notification has a title');
172 | assert
173 | .dom('.message')
174 | .exists();
175 | assert
176 | .dom('.message')
177 | .hasText('message', 'Notification has a message');
178 | assert
179 | .dom('.close-component')
180 | .exists();
181 |
182 | await click('.close-component');
183 |
184 | assert.ok(onClose, 'Close closure action was triggered')
185 | });
186 |
187 | test('close button sends closure action', async function (assert) {
188 | assert.expect(1);
189 |
190 | let onClose = false;
191 | this.actions.onClose = () => (onClose = true);
192 |
193 | await render(template);
194 | await click('.ember-notifier-close-button');
195 |
196 | assert.ok(onClose, 'Close closure action was triggered');
197 | });
198 |
199 | test('#setOption action modifies notification properties', async function (assert) {
200 | assert.expect(1);
201 |
202 | this.owner.register('component:message-component', Component.extend({
203 | layout: hbs`
204 | `
207 | }));
208 |
209 | this.set('notification.contentComponent', 'message-component');
210 |
211 | await render(template);
212 | await click('#update-type');
213 |
214 | assert.equal(this.get('notification.type'), 'is-info', 'Notification type is now info');
215 | });
216 |
217 | test('swipe right to close notification', async function (assert) {
218 | assert.expect(1);
219 |
220 | let onClose = false;
221 | this.actions.onClose = () => (onClose = true);
222 |
223 | await render(template);
224 | await triggerSwipeEvent(find('.ember-notifier-notification-base'), 'right');
225 |
226 | assert.ok(onClose, 'Close closure action was triggered by swipe right');
227 | });
228 | });
229 |
--------------------------------------------------------------------------------
/addon/services/notifier.js:
--------------------------------------------------------------------------------
1 | import { getOwner } from '@ember/application';
2 | import { A } from '@ember/array';
3 | import EmberObject, { computed } from '@ember/object';
4 | import { assign } from '@ember/polyfills';
5 | import { cancel, later, run } from '@ember/runloop';
6 | import Service from '@ember/service';
7 |
8 | const defaultConfig = {
9 | position: 'is-top-right',
10 | duration: 4200, // ms
11 | primaryClass: 'is-primary',
12 | primaryIcon: 'fas fa-bell',
13 | infoClass: 'is-info',
14 | infoIcon: 'fas fa-info',
15 | successClass: 'is-success',
16 | successIcon: 'fas fa-check',
17 | warningClass: 'is-warning',
18 | warningIcon: 'fas fa-exclamation',
19 | dangerClass: 'is-danger',
20 | dangerIcon: 'fas fa-fire',
21 | secondaryClass: 'is-secondary',
22 | secondaryIcon: 'fas fa-comment',
23 | showAnimationClass: 'ember-notifier-notification-show',
24 | hideAnimationClass: 'ember-notifier-notification-hide',
25 | animationTimeout: 500, // ms
26 | minSwipeDistance: 150, // pixels
27 | maxSwipeTime: 300, // ms
28 | swipeDirection: 'right', // 'left' or 'right'
29 | };
30 |
31 | /**
32 | * The notifier service is the public API that provides access to displaying, adding, or removing
33 | * notifications.
34 | *
35 | * Usage:
36 | * ```js
37 | * import Controller from '@ember/controller';
38 | * import { inject as service } from '@ember/service';
39 | *
40 | * export default Controller.extend({
41 | * notifier: service(),
42 | * });
43 | * ```
44 | *
45 | * @class NotifierService
46 | * @public
47 | */
48 | export default Service.extend({
49 | /**
50 | * Notification array.
51 | *
52 | * @property notification
53 | * @type {ember/array}
54 | */
55 | notifications: null,
56 |
57 | config: computed(function () {
58 | const config = getOwner(this).resolveRegistration('config:environment').emberNotifier || {};
59 | return assign(defaultConfig, config);
60 | }),
61 |
62 | init() {
63 | this._super(...arguments);
64 | this.set('notifications', A());
65 | },
66 |
67 | /**
68 | * Adds a primary styled notification.
69 | *
70 | * @method primary
71 | * @param {string} message The notification message.
72 | * @param {Object} [options] Optional notification options.
73 | */
74 | primary(message, options) {
75 | this.add(assign({
76 | message,
77 | type: this.get('config.primaryClass'),
78 | icon: this.get('config.primaryIcon'),
79 | }, options));
80 | },
81 |
82 | /**
83 | * Adds an info styled notification.
84 | *
85 | * @method info
86 | * @param {string} message The notification message.
87 | * @param {Object} [options] Optional notification options.
88 | */
89 | info(message, options) {
90 | this.add(assign({
91 | message,
92 | type: this.get('config.infoClass'),
93 | icon: this.get('config.infoIcon'),
94 | }, options));
95 | },
96 |
97 | /**
98 | * Adds a success styled notification.
99 | *
100 | * @method success
101 | * @param {string} message The notification message.
102 | * @param {Object} [options] Optional notification options.
103 | */
104 | success(message, options) {
105 | this.add(assign({
106 | message,
107 | type: this.get('config.successClass'),
108 | icon: this.get('config.successIcon'),
109 | }, options));
110 | },
111 |
112 | /**
113 | * Adds a warning styled notification.
114 | *
115 | * @method warning
116 | * @param {string} message The notification message.
117 | * @param {Object} [options] Optional notification options.
118 | */
119 | warning(message, options) {
120 | this.add(assign({
121 | message,
122 | type: this.get('config.warningClass'),
123 | icon: this.get('config.warningIcon'),
124 | }, options));
125 | },
126 |
127 | /**
128 | * Adds a danger styled notification.
129 | *
130 | * @method danger
131 | * @param {string} message The notification message.
132 | * @param {Object} [options] Optional notification options.
133 | */
134 | danger(message, options) {
135 | this.add(assign({
136 | message,
137 | type: this.get('config.dangerClass'),
138 | icon: this.get('config.dangerIcon'),
139 | }, options));
140 | },
141 |
142 | /**
143 | * Adds a secondary styled notification.
144 | *
145 | * @method secondary
146 | * @param {string} message The notification message.
147 | * @param {Object} [options] Optional notification options.
148 | */
149 | secondary(message, options) {
150 | this.add(assign({
151 | message,
152 | type: this.get('config.secondaryClass'),
153 | icon: this.get('config.secondaryIcon'),
154 | }, options));
155 | },
156 |
157 | /**
158 | * Adds a custom notification.
159 | *
160 | * @method add
161 | * @param {Object} options Notification options.
162 | * @param {string} options.type Styled class name.
163 | * @param {number} options.duration Remove notification after "n" milliseconds. Disable scheduled
164 | * removal with a value of "0".
165 | * @param {string} [options.title] Optional title.
166 | * @param {string} [options.message] Optional message.
167 | * @param {string} [options.contentComponent] Optional content component name.
168 | * @param {string} [options.icon] Optional icon class name or object name.
169 | * @param {string} [options.showAnimationClass] Optional show animation class name.
170 | * @param {string} [options.hideAnimationClass] Optional hide animation class name.
171 | * @param {string} [options.animationTimeout] Optional number of milliseconds before a
172 | * notification is removed.
173 | * @param {number} [options.minSwipeDistance] Number of pixels a swipe right must travel.
174 | * @param {number} [options.maxSwipeTime] Number of milliseconds between touch start and end.
175 | * @param {string} [options.swipeDirection] The swipe direction which will close a notification.
176 | * @param {function} [options.onRemove] Optional callback function when notification is removed.
177 | */
178 | add(options = {}) {
179 | if (!options.message && !options.contentComponent) {
180 | throw new Error('No message or contentComponent set.');
181 | }
182 |
183 | const defaultOptions = EmberObject.create({
184 | type: this.get('config.primaryClass'),
185 | duration: this.get('config.duration'),
186 | animationState: this.get('config.showAnimationClass'),
187 | animationTimeout: this.get('config.animationTimeout'),
188 | minSwipeDistance: this.get('config.minSwipeDistance'),
189 | maxSwipeTime: this.get('config.maxSwipeTime'),
190 | swipeDirection: this.get('config.swipeDirection'),
191 | timer: null,
192 | onRemove: () => void 0,
193 | });
194 |
195 | const notification = assign(defaultOptions, options);
196 | this.get('notifications').insertAt(0, notification);
197 |
198 | if (notification.duration > 0) {
199 | this.scheduleRemoval(notification);
200 | }
201 | },
202 |
203 | /**
204 | * Removes a notification.
205 | *
206 | * @method remove
207 | * @param {Object} notification The notification to remove.
208 | */
209 | remove(notification) {
210 | this.cancelRemoval(notification);
211 | run(this, () => notification.set('animationState', this.get('config.hideAnimationClass')));
212 | later(this, () => {
213 | notification.onRemove();
214 | this.get('notifications').removeObject(notification);
215 | }, notification.get('animationTimeout'));
216 | },
217 |
218 | /**
219 | * Removes all notifications.
220 | *
221 | * @method empty
222 | */
223 | empty() {
224 | this.get('notifications').forEach(notification => this.remove(notification));
225 | },
226 |
227 | /**
228 | * Schedules removal of a notification based on a duration property.
229 | *
230 | * @method scheduleRemoval
231 | * @param {Object} notification The notification to schedule the removal on.
232 | */
233 | scheduleRemoval(notification) {
234 | const timer = later(this, () => this.remove(notification), notification.get('duration'));
235 | notification.set('timer', timer);
236 | },
237 |
238 | /**
239 | * Cancels a scheduled removal of a notification.
240 | *
241 | * @method cancelRemoval
242 | * @param {Object} notification The notification to cancel the timer on.
243 | */
244 | cancelRemoval(notification) {
245 | cancel(notification.get('timer'));
246 | },
247 | });
248 |
--------------------------------------------------------------------------------
/app/styles/ember-notifier.scss:
--------------------------------------------------------------------------------
1 | // Reference: https://github.com/finnp/dom-notifications
2 | $ember-notifier-z-index: 999999 !default;
3 | $ember-notifier-padding: 1rem !default;
4 | $ember-notifier-text-color: #ffffff !default;
5 | $ember-notifier-default-background-color: #ffffff !default;
6 | $ember-notifier-primary-background-color: #00d1b2 !default;
7 | $ember-notifier-info-background-color: #3ea2ff !default;
8 | $ember-notifier-success-background-color: #64ce83 !default;
9 | $ember-notifier-warning-background-color: #ff7f48 !default;
10 | $ember-notifier-danger-background-color: #e74c3c !default;
11 | $ember-notifier-secondary-background-color: #dbdbdb !default;
12 | $ember-notifier-width: 100% !default;
13 | $ember-notifier-max-width: 420px !default;
14 | $ember-notifier-max-height: 640px !default;
15 | $ember-notifier-border-radius: 4px !default;
16 | $ember-notifier-margin: 0.25rem 0 !default;
17 |
18 | $ember-notifier-icon-color: rgba(255, 255, 255, .74) !default;
19 | $ember-notifier-icon-width: 30px !default;
20 | $ember-notifier-icon-primary-background-color: darken($ember-notifier-primary-background-color, 8%) !default;
21 | $ember-notifier-icon-info-background-color: darken($ember-notifier-info-background-color, 8%) !default;
22 | $ember-notifier-icon-success-background-color: darken($ember-notifier-success-background-color, 8%) !default;
23 | $ember-notifier-icon-warning-background-color: darken($ember-notifier-warning-background-color, 8%) !default;
24 | $ember-notifier-icon-danger-background-color: darken($ember-notifier-danger-background-color, 8%) !default;
25 | $ember-notifier-icon-secondary-background-color: darken($ember-notifier-secondary-background-color, 8%) !default;
26 |
27 | $ember-notifier-content-padding: 5px 10px !default;
28 | $ember-notifier-content-margin: 0 30px !default;
29 | $ember-notifier-title-margin: 0 !default;
30 | $ember-notifier-message-margin: 0 !default;
31 |
32 | $ember-notifier-close-button-width: $ember-notifier-icon-width !default;
33 | $ember-notifier-close-button-height: $ember-notifier-icon-width !default;
34 | $ember-notifier-close-button-line-height: $ember-notifier-close-button-height !default;
35 | $ember-notifier-close-button-color: $ember-notifier-icon-color !default;
36 | $ember-notifier-close-button-opacity: 0.8 !default;
37 | $ember-notifier-close-button-hover-opacity: 1 !default;
38 | $ember-notifier-close-button-size: 1.5rem !default;
39 |
40 | .ember-notifier {
41 | position: fixed;
42 | z-index: $ember-notifier-z-index;
43 | display: flex;
44 | overflow: hidden;
45 | align-items: stretch;
46 | justify-content: flex-start;
47 | flex-direction: column;
48 | width: $ember-notifier-width;
49 | max-width: $ember-notifier-max-width;
50 | padding: $ember-notifier-padding;
51 | margin: 0 auto;
52 | pointer-events: none;
53 |
54 | // positions
55 | &.is-top {
56 | top: 0;
57 | left: 0;
58 | right: 0;
59 | }
60 |
61 | &.is-top-left {
62 | top: 0;
63 | left: 0;
64 | right: auto;
65 | }
66 |
67 | &.is-top-right {
68 | top: 0;
69 | left: auto;
70 | right: 0;
71 | }
72 |
73 | &.is-bottom {
74 | bottom: 0;
75 | left: 0;
76 | right: 0;
77 | flex-direction: column-reverse;
78 | }
79 |
80 | &.is-bottom-left {
81 | bottom: 0;
82 | left: 0;
83 | right: auto;
84 | flex-direction: column-reverse;
85 | }
86 |
87 | &.is-bottom-right {
88 | bottom: 0;
89 | left: auto;
90 | right: 0;
91 | flex-direction: column-reverse;
92 | }
93 | }
94 |
95 | .ember-notifier-notification-base {
96 | position: relative;
97 | overflow: hidden;
98 | margin: $ember-notifier-margin;
99 | pointer-events: auto;
100 | }
101 |
102 | .ember-notifier-notification {
103 | display: block;
104 | max-height: $ember-notifier-max-height;
105 | border-radius: $ember-notifier-border-radius;
106 | color: $ember-notifier-text-color;
107 | background-color: $ember-notifier-default-background-color;
108 |
109 | // colors
110 | &.is-primary {
111 | background-color: $ember-notifier-primary-background-color;
112 |
113 | .ember-notifier-icon {
114 | background-color: $ember-notifier-icon-primary-background-color;
115 | }
116 | }
117 |
118 | &.is-info {
119 | background-color: $ember-notifier-info-background-color;
120 |
121 | .ember-notifier-icon {
122 | background-color: $ember-notifier-icon-info-background-color;
123 | }
124 | }
125 |
126 | &.is-success {
127 | background-color: $ember-notifier-success-background-color;
128 |
129 | .ember-notifier-icon {
130 | background-color: $ember-notifier-icon-success-background-color;
131 | }
132 | }
133 |
134 | &.is-warning {
135 | background-color: $ember-notifier-warning-background-color;
136 |
137 | .ember-notifier-icon {
138 | background-color: $ember-notifier-icon-warning-background-color;
139 | }
140 | }
141 |
142 | &.is-danger {
143 | background-color: $ember-notifier-danger-background-color;
144 |
145 | .ember-notifier-icon {
146 | background-color: $ember-notifier-icon-danger-background-color;
147 | }
148 | }
149 |
150 | &.is-secondary {
151 | background-color: $ember-notifier-secondary-background-color;
152 |
153 | .ember-notifier-icon {
154 | background-color: $ember-notifier-icon-secondary-background-color;
155 | }
156 | }
157 | }
158 |
159 | .ember-notifier-icon {
160 | position: absolute;
161 | top: 0;
162 | left: 0;
163 | width: $ember-notifier-icon-width;
164 | height: 100%;
165 | color: $ember-notifier-icon-color;
166 | text-align: center;
167 |
168 | span {
169 | position: relative;
170 | top: 5px;
171 | }
172 | }
173 |
174 | .ember-notifier-content {
175 | padding: $ember-notifier-content-padding;
176 | margin: $ember-notifier-content-margin;
177 | -ms-word-wrap: break-word;
178 | word-wrap: break-word;
179 | }
180 |
181 | .ember-notifier-title {
182 | margin: $ember-notifier-title-margin;
183 | }
184 |
185 | .ember-notifier-message {
186 | margin: $ember-notifier-message-margin;
187 | }
188 |
189 | .ember-notifier-close {
190 | position: absolute;
191 | top: 0;
192 | right: 0;
193 | text-align: center;
194 | }
195 |
196 | .ember-notifier-close-button {
197 | position: relative;
198 | margin: 0;
199 | width: $ember-notifier-close-button-width;
200 | height: $ember-notifier-close-button-height;
201 | -moz-appearance: none;
202 | -webkit-appearance: none;
203 | appearance: none;
204 | padding: 0;
205 | border: none;
206 | background: transparent;
207 | color: $ember-notifier-close-button-color;
208 | opacity: $ember-notifier-close-button-opacity;
209 | font-size: $ember-notifier-close-button-size;
210 | line-height: $ember-notifier-close-button-line-height;
211 | text-align: center;
212 | cursor: pointer;
213 |
214 | &:hover,
215 | &:focus {
216 | opacity: $ember-notifier-close-button-hover-opacity;
217 | }
218 | }
219 |
220 | /* ----------------------------------------------
221 | * Generated by Animista on 2018-9-21 14:33:11
222 | * w: http://animista.net, t: @cssanimista
223 | * ---------------------------------------------- */
224 | .ember-notifier-notification-show {
225 | -webkit-animation: notification-show 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
226 | animation: notification-show 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
227 | }
228 |
229 | .ember-notifier-notification-hide {
230 | -webkit-animation: notification-hide 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
231 | animation: notification-hide 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
232 | }
233 |
234 | @-webkit-keyframes notification-show {
235 | 0% {
236 | -webkit-transform: scale(0);
237 | transform: scale(0);
238 | opacity: 1;
239 | }
240 | 100% {
241 | -webkit-transform: scale(1);
242 | transform: scale(1);
243 | opacity: 1;
244 | }
245 | }
246 |
247 | @keyframes notification-show {
248 | 0% {
249 | -webkit-transform: scale(0);
250 | transform: scale(0);
251 | opacity: 1;
252 | }
253 | 100% {
254 | -webkit-transform: scale(1);
255 | transform: scale(1);
256 | opacity: 1;
257 | }
258 | }
259 |
260 | @-webkit-keyframes notification-hide {
261 | 0% {
262 | -webkit-transform: scale(1);
263 | transform: scale(1);
264 | opacity: 1;
265 |
266 | }
267 | 100% {
268 | -webkit-transform: scale(0);
269 | transform: scale(0);
270 | opacity: 1;
271 | }
272 | }
273 |
274 | @keyframes notification-hide {
275 | 0% {
276 | -webkit-transform: scale(1);
277 | transform: scale(1);
278 | opacity: 1;
279 | }
280 | 100% {
281 | -webkit-transform: scale(0);
282 | transform: scale(0);
283 | opacity: 1;
284 | }
285 | }
286 |
--------------------------------------------------------------------------------