npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ember-promise-modals

v5.0.0

Published

The better way to handle modals in your Ember.js apps.

Downloads

79,837

Readme

ember-promise-modals

The better way to handle modals in your Ember.js apps.

[!NOTE] ember-promise-modals was written and is maintained by Mainmatter and contributors. We offer consulting, training, and team augmentation for Ember.js – check out our website to learn more!

Compatibility

  • Ember.js v3.28 or above
  • Ember CLI v3.28 or above
  • Node.js v16 or above

Installation

ember install ember-promise-modals

Usage

To use EPM in your project, add the target for the modals to your application.hbs:

<EpmModalContainer />

Then you can inject the modals service wherever you need and call its open method with a component class to render it as a modal.

import { inject as service } from '@ember/service';
import ConfirmationModal from 'my-app/components/confirmation-modal';

export default class extends Component {
  @service modals;

  @action
  async handleOpenModal() {
    let modal = this.modals.open(ConfirmationModal);

    // the instance acts as a promise that resolves with anything passed to the @close function
    modal.then(results => {
      // do something with the data
    });

    // so does `await`ing it!
    let results = await modal;

    // you can also close the modal from outside
    modal.close();
  }
}
<button type='button' {{on 'click' this.handleOpenModal}}>
  Click Me!
</button>

Passing data to the rendered component

You can pass custom data into your rendered template like so:

this.modals.open(FilePreviewModal, {
  fileUrl: this.fileUrl,
});

All passed attributes can be accessed from the passed-in data object:

<!-- components/file-preview.hbs -->
<img src={{@data.fileUrl}} />
// components/file-preview.js
this.args.data.fileUrl; // or this.data.fileUrl in classic components

NOTE: By default, a close method is passed in your rendered component, in order to trigger the "close modal" action. It can be called like so:

<!-- components/file-preview.hbs -->
<button type='button' {{on 'click' (fn @close results)}}>Close</button>
// components/file-preview.js
this.args.close(results); // or this.close() in classic components

Routable modals using the template helper

This addon comes with a {{open-modal}} template helper which can be used to trigger modals from any templates. It accepts the similar arguments as the open method. It can be used to open a modal in a route, closing it automatically when navigating elsewhere.

{{open-modal this.ConfirmationModalComponent (hash fileUrl=this.fileUrl) close=(fn this.save results)}}

Positional arguments mimick the open() method on the service:

  • componentClass: An imported component class of the modal to render
  • data: Pass additional context to the modal,
  • options: Pass additional options to the modal

Named arguments:

  • close is called asynchronously with the data returned by the modals @close action when it is closed

Animation

This addon uses CSS animations. You can either replace the styles of this addon with your own or adjust the defaults using CSS custom properties in your :root{} declaration or in the CSS of any parent container of <EpmModalContainer />.

Available properties and their defaults can be found in the :root {} block inside the addons css.

By default, the animations are dropped when prefers-reduced-motion is detected.

Custom animations

To override the animation for a specific modal, an options object containing a custom className can be handed to the .open() method.

this.modals.open(
  FilePreviwModal,
  {
    fileUrl: this.fileUrl,
  },
  {
    // custom class, see below for example
    className: 'custom-modal',
    // optional: name the animation triggered by the custom css class
    //           animations ending in "-out" are detected by default!
    //           You most likely do not have to do this unless you absolutely
    //           can't have an animation ending in '-out'
    animationKeyframesOutName: 'custom-animation-name-out',
    // optional: a hook that is called when the closing animation of
    //           the modal (so not the backdrop) has finished.
    onAnimationModalOutEnd: () => {},
  },
);
.custom-modal {
  animation: custom-animation-in 0.5s;
  opacity: 1;
  transform: translate(0, 0);
}

/* 
  The `.epm-out` class is added to the parent of the modal when the modal 
  should be closed, which triggers the animation
*/
.custom-modal.epm-out {
  animation: custom-animation-name-out 0.2s; /* default out animation is 2s */
  opacity: 0;
  transform: translate(0, 100%);
}

/* 
  animation name has to end in "-out" to be detected by the custom animationend 
  event handler 
*/
@keyframes custom-animation-name-out {
  0% {
    opacity: 1;
    transform: translate(0, 0);
  }
  100% {
    opacity: 0;
    transform: translate(0, 100%);
  }
}

The CSS animations which are applied by the custom CSS class must end in -out to make the animations trigger the modal removal.

Examples

Examples for custom animations and how to apply them can be found in the addon's dummy application.

See the application.js controller for how the modals are opened in your JavaScript actions and look at app.css for the style definition of these custom animations.

Accessibility

User can press the Esc key to close the modal.

EPM uses focus-trap internally in order to handle user focus.

EPM will ensure to focus the first "tabbable element" by default. If no focusable element is present, focus will be applied on the currently visible auto-generated container for the current modal.

Focus Trap can be configured both on the modals service, and the individual modal level when calling this.modals.open(). Global and local options are used in that order, which means that local config take precedence.

To set global Focus Trap config that all modals inherit, override the default Modals service by extending it, place it to app/services/modals.js, then use the focusTrapOptions property:

import BaseModalsService from 'ember-promise-modals/services/modals';

export default class ModalsService extends BaseModalsService {
  focusTrapOptions = {
    clickOutsideDeactivates: false,
  };
}

Example for local Focus Trap option, when opening a specific modal:

this.modals.open(
  FilePreviewModal,
  { fileUrl: this.fileUrl },
  {
    focusTrapOptions: {
      clickOutsideDeactivates: false,
    },
  },
);

To disable Focus Trap completely, set focusTrapOptions to null on the modals service:

import BaseModalsService from 'ember-promise-modals/services/modals';

export default class ModalsService extends BaseModalsService {
  focusTrapOptions = null;
}

Or when opening a modal:

this.modals.open(
  FilePreviewModal,
  { fileUrl: this.fileUrl },
  {
    focusTrapOptions: null,
  },
);

⚠️ We strongly advise against doing this. This will in most cases worsen the accessibility of modals for your users. Be very careful.

Testing

This addon provides a test helper function that reduces the timing for the CSS transitions to near zero to speed up your tests.

import { setupPromiseModals } from 'ember-promise-modals/test-support';

module('Application | ...', function (hooks) {
  // ...
  setupPromiseModals(hooks);
  // ...
});

Migration guide

See the Migration guide for details:

  • From 0.x.x to 1.x.x about the replacement of ember-animated powered animations with CSS-based animations.
  • From v4.x.x to v5.x.x about the removal of the PostCSS process.

Contributing

See the Contributing guide for details.

License

ember-promise-modals is developed by and © Mainmatter GmbH and contributors. It is released under the MIT License.