Package detail

ember-modal-dialog

yapplabs81kMIT4.1.5

An ember-cli addon for implementing modal dialogs

ember-addon

readme

Ember Modal Dialog Build Status Ember Observer Score

Regarding the Status of this Addon

A lot has happened in browser APIs, the JS ecosytem, and in the Ember ecosystem since this addon was created.

Most notably, all major browsers have added support for the HTML dialog element. This effectively delivers the core use case for this addon as a browser API. It is not syntax-compatible of course, so adopting it requires learning the new element and its programmatic API and using it as appropriate for your situation.

In the Javascript ecosystem, tether.js has reached the end of its life, being succeeded by popper and now by floating-ui as the most common solution for positioning a DOM element relative to another element. Our experience is that floating-ui works great in conjunction with Ember's modifiers. While there are addons that wrap it, like ember-velcro, we have found that using it directly does the job quite well.

In the Ember ecosystem, addon authors are transitioning addons to support modern versions of Ember and to become v2 addons. This addon has three dependencies that are either unsupported or poorly supported: ember-tether, liquid-wormhole, and liquid-tether.

Having contemplated these changes, we won't be investing in ongoing development of this addon, and are migrating away from it in our own projects. We will still review and accept PRs for bug fixes and compatibility, but we encourage you to move on.

Overview

The ember-modal-dialog addon provides components to implement modal dialogs throughout an Ember application using a simple, consistent pattern.

Unlike some other modal libraries for Ember, ember-modal-dialog uses solutions like ember-wormhole to render a modal structure as a top-level DOM element for layout purposes while retaining its logical position in the Ember view hierarchy. This difference introduces a certain elegance and, dare we say, joy, into the experience of using modals in your app. For more info on this, see the "Wormhole" section below.

Table of Contents

Live Demo and Test Examples

View a live demo here: http://yapplabs.github.io/ember-modal-dialog/

Test examples are located in tests/dummy/app/templates/application.hbs and can be run locally by following the instructions in the "Installation" and "Running" sections below.

Video image

Including In An Ember Application

Here is the simplest way to get started with ember-modal-dialog:

ember install ember-modal-dialog

Then import the CSS files

app.css

@import "ember-modal-dialog/ember-modal-structure.css";
@import "ember-modal-dialog/ember-modal-appearance.css";

If you’re using SASS then just import the CSS slightly differently

app.scss

@import "ember-modal-dialog/ember-modal-structure";
@import "ember-modal-dialog/ember-modal-appearance";

application.hbs

{{#modal-dialog}}
  Oh hai there!
{{/modal-dialog}}

Upgrading

Earlier versions of ember-modal-dialog required ember-cli-sass and an app.scss file to import styling.

Please be aware this is no longer the case.

Existing applications should continue to work correctly but if you were using ember-cli-sass solely due to ember-modal-dialog it might be worthwhile removing ember-cli-sass completely and just importing the styles directly into app.css instead, as shown above.

Controller-bound Usage

Here is a more useful example of how to conditionally display a modal based on a user interaction.

Template

<button {{action (action "toggleModal")}}>Toggle Modal</button>

{{#if isShowingModal}}
  {{#modal-dialog
      onClose=(action "toggleModal")
      targetAttachment="center"
      translucentOverlay=true
  }}
    Oh hai there!
  {{/modal-dialog}}
{{/if}}

Controller

import Controller from '@ember/controller';

export default Controller.extend({
  isShowingModal: false,
  actions: {
    toggleModal() {
      this.toggleProperty('isShowingModal');
    }
  }
});

Routable Usage

To have a modal open for a specific route, just drop the {{modal-dialog}} into that route's template. Don't forget to have an {{outlet}} on the parent route.

Configurable Properties

modal-dialog

The modal-dialog component supports the following properties:

Property Purpose
hasOverlay Toggles presence of overlay div in DOM
translucentOverlay Indicates translucence of overlay, toggles presence of translucent CSS selector
onClose The action handler for the dialog's onClose action. This action triggers when the user clicks the modal overlay.
onClickOverlay An action to be called when the overlay is clicked. If this action is specified, clicking the overlay will invoke it instead of onClose.
clickOutsideToClose Indicates whether clicking outside a modal without an overlay should close the modal. Useful if your modal isn't the focus of interaction, and you want hover effects to still work outside the modal.
renderInPlace A boolean, when true renders the modal without wormholing or tethering, useful for including a modal in a style guide
overlayPosition either 'parent' or 'sibling', to control whether the overlay div is rendered as a parent element of the container div or as a sibling to it (default: 'parent')
containerClass CSS class name(s) to append to container divs. Set this from template.
containerClassNames CSS class names to append to container divs. If you subclass this component, you may define this in your subclass.)
overlayClass CSS class name(s) to append to overlay divs. Set this from template.
overlayClassNames CSS class names to append to overlay divs. If you subclass this component, you may define this in your subclass.)
wrapperClass CSS class name(s) to append to wrapper divs. Set this from template.
wrapperClassNames CSS class names to append to wrapper divs. If you subclass this component, you may define this in your subclass.)
animatable A boolean, when true makes modal animatable using liquid-fire (requires liquid-wormhole to be installed, and for tethering situations liquid-tether. Having these optional dependencies installed and not specifying animatable will make animatable=true be the default.)

Tethering

If you specify a tetherTarget, you are opting into "tethering" behavior, and you must have either ember-tether or liquid-tether installed.

Property Purpose
tetherTarget Element selector or element reference for that serves as the reference for modal position

We use the amazing Tether.js library (via ember-tether) to let you position your dialog relative to other elements in the DOM.

* Please see Hubspot Tether for usage documentation.

When in a tethering scenario, you may also pass the following properties, which are passed through to Tether:

Property Purpose
attachment Delegates to Hubspot Tether*
targetAttachment Delegates to Hubspot Tether*
tetherClassPrefix Delegates to Hubspot Tether*
offset Delegates to Hubspot Tether*
targetOffset Delegates to Hubspot Tether*
constraints Delegates to Hubspot Tether*

Animation

This component supports animation when certain addons are present (liquid-wormhole, liquid-tether).

Detection is be automatic. To opt out of using animatable features when you have these liquid-* addons installed, pass animatable=false.

When in an animatable scenario, you may also pass the following properties, which are passed through to liquid-wormhole or liquid-tether:

Property Purpose
stack Delegates to liquid-wormhole/liquid-tether

Optional Dependencies

Dependency Documentation
ember install ember-tether Docs
ember install liquid-wormhole Docs
ember install liquid-tether Docs
ember install liquid-fire Docs

Which Component Should I Use?

Various modal use cases are best supported by different DOM structures. Ember Modal Dialog's modal-dialog component provides the following capabilities:

  • modal-dialog without passing a tetherTarget: Uses ember-wormhole to append the following parent divs to the destination element: wrapper div > overlay div > container div

This can be customized (see overlayPosition).

  • modal-dialog, with a tetherTarget provided: Uses ember-tether to display modal container div. Uses ember-wormhole to append optional overlay div to the destination element. Requires separate installation of ember-tether dependency.

Positioning

With the default CSS provided, your modal will be centered in the viewport. By adjusting the CSS, you can adjust this logic.

Pass a tetherTarget in order to position our modal in relation to the target and enable your modal remain positioned near their targets when users scroll or resize the window.

Use attachment and targetAttachment properties to configure positioning of the modal dialog near its target. Ember Modal Dialog uses the syntax from Hubspot Tether for these properties: "top|middle|bottom left|center|right|elementCenter"... e.g. 'middle left'

To enable this behavior, install ember-tether as a dependency of your ember app.

ember install ember-tether

Then pass a selector as tetherTarget for the modal you wish to position this way:

{{#modal-dialog
    tetherTarget='#target-element-id'
    targetAttachment='middle right'
    attachment='middle left'
}}
  I am a modal that will remain tethered to the right of the element with id 'target-element-id'
{{/modal-dialog}}

Caveats

Event delegation originating from content inside ember-tether blocks will only work for Ember apps that use Ember's default root element of the body tag. This is because, generally speaking, the Hubspot Tether library appends its positioned elements to the body element.

If you are not overriding the default root element, then don't worry and carry on. ember-tether will work just fine for you.

Wormholes

Display of a modal dialog is typically triggered by a user interaction. While the content in the dialog is related to the content in the user interaction, the underlying display mechanism for the dialogs can be shared across the entire application.

The add-modals-container initializer appends a container element to the application.rootElement. It injects a reference to this container element id as a property of the modal-dialog service, which is then used in the modal-dialog component. The property is injected into a service instead of directly into the modal-dialog component to make it easier to extend the component and make custom modals.

ember-modal-dialog uses ember-wormhole to append modal overlays and contents to a dedicated element in the DOM. This decouples the DOM location of a modal from the DOM location of whatever triggered its display... hence wormholes!

Configuring the Modal Root Element Id

This default id of the modal root element is modal-overlays and can be overridden in environment application options as follows:

environment.js

module.exports = function(environment) {
  var ENV = {
    // ...
    APP: {
      // ...
      emberModalDialog: {
        modalRootElementId: 'custom-modal-root-element'
      }
    }
  };
  // ...

  return ENV;
};

Configuring Styles

You can import the CSS files directly

app.css

@import "ember-modal-dialog/ember-modal-structure.css";
@import "ember-modal-dialog/ember-modal-appearance.css";

If you’re using SASS then just import the CSS slightly differently

app.scss

@import "ember-modal-dialog/ember-modal-structure";
@import "ember-modal-dialog/ember-modal-appearance";

Keyboard shortcuts

A quick-and-dirty way to implement keyboard shortcuts (e.g. to dismiss your modals with escape) is to subclass the dialog and attach keyboard events:

// app/components/modal-dialog.js
import ModalDialog from 'ember-modal-dialog/components/modal-dialog';

const ESC_KEY = 27;

export default ModalDialog.extend({
  didInsertElement() {
    this._super(...arguments);
    this._initEscListener();
  },

  willDestroyElement(){
    this._super(...arguments);
    Ember.$('body').off('keyup.modal-dialog');
  },

  _initEscListener() {
    const closeOnEscapeKey = (ev) => {
      if (ev.keyCode === ESC_KEY) { get(this, 'onClose')(); }
    };

    Ember.$('body').on('keyup.modal-dialog', closeOnEscapeKey);
  },
});

This can work, but some apps require a more sophisticated approach. One approach takes advantage of the ember-keyboard library. Here's an example:

// app/components/modal-dialog.js
import { on } from '@ember/object/evented';
import ModalDialog from 'ember-modal-dialog/components/modal-dialog';
import { EKMixin as EmberKeyboardMixin, keyDown } from 'ember-keyboard';

export default ModalDialog.extend(EmberKeyboardMixin, {
  init() {
    this._super(...arguments);

    set(this, 'keyboardActivated', true);
  },

  closeOnEsc: on(keyDown('Escape'), function() {
    get(this, 'onClose')();
  })
});

View the library for more information.

iOS

In order for taps on the overlay to be functional on iOS, a cursor: pointer style is added to the div when on iOS. If you need to change this behavior, subclass modal-dialog and override makeOverlayClickableOnIOS.

Custom Modals

If you have various different styles of modal dialog in your app, it can be useful to subclass the dialog as a new component:

// app/components/full-screen-modal.js

import ModalDialog from 'ember-modal-dialog/components/modal-dialog';

export default ModalDialog.extend({
  containerClassNames: "full-screen-modal",
  targetAttachment: "none"
});

By subclassing modal-dialog your component will use the default modal dialog template. Therefore, you do not need to create a app/templates/components/full-screen-modal.hbs file. Your component can then be used like so:

{{#full-screen-modal}}
  Custom modal contents
{{/full-screen-modal}}

Using as a nested addon

If you create an addon that you want to depend on ember-modal-dialog, you need to provide for ember-modal-dialog's config hook to run. You do this in the config hook of your addon. Example:

// index.js

module.exports = {
  name: 'my-addon',
  config(environment, appConfig) {
    let initialConfig = _.merge({}, appConfig);
    let updatedConfig = this.addons.reduce((config, addon) => {
      if (addon.config) {
        _.merge(config, addon.config(environment, config));
      }
      return config;
    }, initialConfig);
    return updatedConfig;
  }
};

Compatibility & Dependencies

  • For Ember versions >= 4.0, liquid-tether is not compatible so the animated+tethered variation of ember-modal-dialog will not work
  • For Ember versions >= 3.20, use the latest published version
  • For Ember versions >= 2.8 and < 3.20, use the latest 3.x version (Note that ember-cli >= 2.13 is required, though your ember version may be >= 2.8)
  • For Ember versions >= 2.4 and < 2.8, use the latest 2.x version
  • For Ember versions >= 1.10 and < 2.4, use ember-modal-dialog 1.0.0 (Due to a bug in these versions of Ember, you may have trouble with Ember 1.13.7, 1.13.8 and 1.13.9 -- See #71)

Additional Resources

Contributing

See the Contributing guide for details.

Credits

Contributions from @stefanpenner, @krisselden, @chrislopresto, @lukemelia, @raycohen, @andrewhavens, @samselikoff and others. Yapp Labs was an Ember.js consultancy based in NYC, that has since been folded into Yapp.

changelog

v4.1.5 (2024-12-07)

:bug: Bug Fix

  • #404 Move items from devDeps to peerDeps and update peerDepsMeta to clarify dependencies (@lukemelia)

:memo: Documentation

:house: Internal

Committers: 2

v4.1.4 (2024-06-28)

:bug: Bug Fix

  • #400 Avoid Fastboot crash when accessing navigator global (@gilest)

Committers: 1

v4.1.3 (2024-01-04)

v4.1.2 (2023-05-08)

:bug: Bug Fix

  • #395 ember-tether version change was missing in peerDependencies declaration (@lukemelia)

Committers: 1

v4.1.1 (2023-05-05)

v4.1.0 (2023-02-23)

:rocket: Enhancement

  • #389 Add splattibutes support and fix containerClassNames for rendered in place modals (@ratierd)

:bug: Bug Fix

  • #389 Add splattibutes support and fix containerClassNames for rendered in place modals (@ratierd)

:house: Internal

Committers: 2

v4.0.1 (2022-04-10)

:bug: Bug Fix

  • #378 Fix error attempting to join class names when they are a string (@lukemelia)

Committers: 2

v4.0.0 (2022-02-09)

:boom: Breaking Change

Drops support for Ember 3.19 and below

:rocket: Enhancement

Ember 4 compatibility and Embroider support

v4.0.0-beta.0 (2022-01-24)

:rocket: Enhancement

:house: Internal

Committers: 1

v4.0.0-alpha.1 (2021-09-09)

:boom: Breaking Change

:house: Internal

Committers: 3

v4.0.0-alpha.0 (2021-05-25)

:boom: Breaking Change

:house: Internal

Committers: 3

v3.0.3 (2021-05-17)

:bug: Bug Fix

  • #326 initializers/add-modals-container: Work around string injection bug (@Turbo87)

:house: Internal

Committers: 1

v3.0.2 (2021-02-18)

:bug: Bug Fix

Committers: 1

v3.0.1 (2020-07-24)

:bug: Bug Fix

:memo: Documentation

:house: Internal

Committers: 5

Change Log

3.0.0-beta.4 (2019-06-18)

Full Changelog

Merged pull requests:

v3.0.0-beta.3 (2018-11-23)

Full Changelog

Merged pull requests:

  • Only try to invoke onClose action if it has been provided #271 (lukemelia)

v3.0.0-beta.2 (2018-11-12)

Full Changelog

Merged pull requests:

  • [BREAKING] Update to ember-cli-babel@7. (Drops support for ember-cli \< 2.13) #266 (rwjblue)

v3.0.0-beta.1 (2018-11-12)

Full Changelog

Merged pull requests:

v2.4.4 (2018-05-14)

Full Changelog

v3.0.0-beta.0 (2018-03-03)

Full Changelog

Merged pull requests:

v2.4.3 (2018-03-03)

Full Changelog

Merged pull requests:

  • Fix bug in specifying incorrect overlay selector on iOS #245 (lukemelia)

v2.4.2 (2018-02-22)

Full Changelog

Merged pull requests:

v2.4.1 (2017-12-05)

Full Changelog

Merged pull requests:

v2.4.0 (2017-11-21)

Full Changelog

Merged pull requests:

v2.3.0 (2017-06-23)

Full Changelog

Merged pull requests:

v2.2.0 (2017-05-15)

Full Changelog

Merged pull requests:

  • Make modal-dialog component animatable via liquid-fire #193 (lukemelia)
  • Add support for an overlayPosition property which supports values of 'parent' or 'sibling' #192 (lukemelia)
  • Allow specifying hasOverlay=false to suppress output of the overlay div. #191 (lukemelia)
  • Minor README cleanup #190 (lukemelia)

v2.1.0 (2017-05-14)

Full Changelog

Merged pull requests:

  • Detect ember-tether and throw an error early if not present and tetherTarget is passed. #189 (lukemelia)
  • Make tests more robust with waitUntil #188 (lukemelia)
  • Simplify dummy app a bit #187 (lukemelia)
  • Unify modal-dialog and tether-dialog into one modal-dialog component #186 (lukemelia)
  • Replace modal-dialog-overlay component with a div and deprecate it. #185 (lukemelia)
  • Update title of dummy app #184 (lukemelia)
  • Rename close action to onClose and deprecate close. #183 (lukemelia)
  • Switch dummy app/tests to use routes instead of query params #182 (lukemelia)
  • install was installed inadvertently, so let's uninstall the inadvertent install of install #181 (lukemelia)
  • Use ember-native-dom-helpers and async/await #180 (lukemelia)
  • Upgrade ember-cli, ember-cli-babel, and friends #179 (lukemelia)
  • Use camelCase properties, deprecate public kebab case properties #178 (lukemelia)

v2.0.0 (2017-05-12)

Full Changelog

Merged pull requests:

v2.0.0-beta.0 (2017-04-05)

Full Changelog

Merged pull requests:

  • Update ember-cli and other tooling dependencies. #174 (lukemelia)
  • Update ember-wormhole dependency and ember-tether devDependency #173 (lukemelia)
  • Drop support for Ember versions older than 2.4 #172 (lukemelia)

v1.0.0 (2017-04-05)

Full Changelog

Merged pull requests:

  • Use Chrome in CI. #171 (lukemelia)
  • [BREAKING] Change default scss to use static positioning for render in place. #169 (andrewhavens)
  • Use yarn for dependency management #168 (andrewhavens)
  • Add ability to specify a callback that is triggered when overlay is clicked. #167 (andrewhavens)
  • Check for Ember version with new ember-cli-version-checker API. #166 (dan-ste)

v0.9.1 (2017-02-14)

Full Changelog

Merged pull requests:

  • Touch event handling on IOS for clickOutsideToClose #161 (Chee7ah)
  • Add element center to positioned container #144 (wandertosee)
  • Register unique clickOutsideToClose click handlers #129 (oscarni)

v0.9.0 (2016-08-19)

Full Changelog

Merged pull requests:

  • Support unit and integration tests out of the box. #142 (blimmer)
  • Update README to change usage of ember-key-responder to ember-keyboard #141 (SaladFork)
  • Update ember-cli and use ember try's versionCompatibility #136 (lukemelia)

v0.8.8 (2016-07-13)

Full Changelog

Merged pull requests:

  • Move back to ember-wormhole 0.3.6 to retain support for older Ember versions #135 (lukemelia)

v0.8.7 (2016-07-12)

Full Changelog

Merged pull requests:

  • Avoid deprecation warning by updating ember-cli-htmlbars #134 (mdentremont)

v0.8.6 (2016-07-05)

Full Changelog

Merged pull requests:

v0.8.5 (2016-06-22)

Full Changelog

Merged pull requests:

  • Call this.\_super.init to avoid ember-cli deprecation #132 (ascot21)

v0.8.4 (2016-05-11)

Full Changelog

Merged pull requests:

v0.8.3 (2015-12-22)

Full Changelog

Merged pull requests:

v0.8.2 (2015-10-26)

Full Changelog

Merged pull requests:

v0.8.1 (2015-09-19)

Full Changelog

Merged pull requests:

v0.8.0 (2015-09-10)

Full Changelog

Merged pull requests:

v0.7.7 (2015-08-04)

Full Changelog

Merged pull requests:

  • refactor updateAlignment to make it clear what the different paths are #66 (raycohen)
  • assert that specified alignment target exists #65 (kagemusha)
  • Adds the ability to close when clicking anywhere outside the modal without an overlay #59 (kellyselden)
  • Add version check for minimum ember-cli version #56 (lukemelia)

v0.7.6 (2015-07-27)

Full Changelog

Merged pull requests:

v0.7.5 (2015-06-25)

Full Changelog

Merged pull requests:

v0.7.4 (2015-06-19)

Full Changelog

Merged pull requests:

  • Allow 2.0.0-beta's to use current templates. #44 (rwjblue)

v0.7.3 (2015-06-19)

Full Changelog

Merged pull requests:

  • Swap the modal-dialog template based on Ember version. #43 (rwjblue)

v0.7.2 (2015-05-31)

Full Changelog

v0.7.1 (2015-05-24)

Full Changelog

Merged pull requests:

  • When renderInPlace is true, do not use tether or do any positioning #36 (lukemelia)

v0.7.0 (2015-05-19)

Full Changelog

Merged pull requests:

v0.6.0 (2015-05-05)

Full Changelog

Merged pull requests:

  • Add modal-dialog service with destinationElementId for easier subclassing #28 (rlivsey)
  • Improve routable usage info. #27 (pedrokiefer)
  • Component unit test example #20 (varblob)

v0.5.0 (2015-04-28)

Full Changelog

Merged pull requests:

v0.4.1 (2015-04-25)

Full Changelog

v0.4.0 (2015-04-17)

Full Changelog

v0.3.0 (2015-04-13)

Full Changelog

Merged pull requests:

  • Introduced overlay-class and container-class attributes. #18 (lukemelia)
  • Allow alignmentTarget to be specified as a reference to an Ember View or a DOM element in addition to a selector. #16 (lukemelia)

v0.2.2 (2015-04-11)

Full Changelog

v0.2.1 (2015-04-10)

Full Changelog

Merged pull requests:

  • Remove orphan overlay component #13 (olivia)
  • Updating ember-cli version to fix build errors on windows #12 (olivia)
  • Clarify key-responder is about keyboard shortcuts #10 (samselikoff)

0.2.0 (2015-04-05)

Full Changelog

Merged pull requests:

  • Test against 1.10 through beta #7 (ef4)
  • Drop unused ember-data dependency #6 (ef4)
  • Use Ember.observer and Ember.on instead of function prototype equivalents #4 (lukemelia)

0.1.0 (2015-03-26)

Full Changelog

Merged pull requests:

0.0.3 (2014-11-14)

Full Changelog

0.0.2 (2014-10-12)

Full Changelog

0.0.1 (2014-10-05)

* This Change Log was automatically generated by github_changelog_generator