├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc.js ├── .template-lintrc.js ├── .watchmanconfig ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── RELEASE.md ├── addon ├── components │ ├── basic-dialog.hbs │ ├── basic-dialog.js │ ├── in-place-dialog.hbs │ ├── in-place-dialog.js │ ├── liquid-dialog.hbs │ ├── liquid-dialog.js │ ├── liquid-tether-dialog.hbs │ ├── liquid-tether-dialog.js │ ├── modal-dialog.hbs │ ├── modal-dialog.js │ ├── overlay.hbs │ ├── overlay.js │ ├── positioned-container.js │ ├── tether-dialog.hbs │ └── tether-dialog.js ├── helpers │ └── ignore-children.js ├── instance-initializers │ └── add-modals-container.js └── utils │ └── config-utils.js ├── app ├── components │ ├── ember-modal-dialog-positioned-container.js │ ├── ember-modal-dialog │ │ ├── -basic-dialog.js │ │ ├── -in-place-dialog.js │ │ ├── -liquid-dialog.js │ │ ├── -liquid-tether-dialog.js │ │ ├── -tether-dialog.js │ │ └── overlay.js │ └── modal-dialog.js ├── helpers │ └── ignore-children.js ├── instance-initializers │ └── add-modals-container.js ├── services │ └── modal-dialog.js └── styles │ └── ember-modal-dialog │ ├── ember-modal-appearance.css │ └── ember-modal-structure.css ├── codemods.log ├── config └── environment.js ├── ember-cli-build.js ├── index.js ├── package.json ├── patches └── ember-shiki@0.3.0.patch ├── pnpm-lock.yaml ├── testem.js ├── tests ├── acceptance │ ├── animatable-test.js │ ├── basic-test.js │ ├── tethered-animatable-test.js │ └── tethered-test.js ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ ├── demo-modal.hbs │ │ │ ├── demo-modal.js │ │ │ ├── demo-modal │ │ │ │ ├── button.hbs │ │ │ │ └── button.js │ │ │ ├── my-cool-modal-dialog-two.js │ │ │ └── my-cool-modal-dialog.js │ │ ├── controllers │ │ │ ├── animatable.js │ │ │ ├── index.js │ │ │ ├── tethered-animatable.js │ │ │ └── tethered.js │ │ ├── helpers │ │ │ └── lorem-ipsum.js │ │ ├── index.html │ │ ├── router.js │ │ ├── styles │ │ │ └── app.css │ │ ├── templates │ │ │ ├── animatable.hbs │ │ │ ├── application.hbs │ │ │ ├── index.hbs │ │ │ ├── tethered-animatable.hbs │ │ │ └── tethered.hbs │ │ ├── transitions.js │ │ └── utils │ │ │ └── code-snippets │ │ │ ├── animatable.js │ │ │ ├── index.js │ │ │ ├── tethered-animatable.js │ │ │ └── tethered.js │ ├── config │ │ ├── deprecation-workflow.js │ │ ├── ember-cli-update.json │ │ ├── ember-try.js │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ ├── modal-dialog.png │ │ ├── robots.txt │ │ └── tether-dialog.png ├── helpers │ ├── .gitkeep │ └── modal-asserts.js ├── index.html ├── integration │ └── components │ │ ├── ember-modal-dialog-positioned-container-test.js │ │ ├── ember-modal-dialog │ │ ├── -basic-dialog-test.js │ │ ├── -in-place-dialog-test.js │ │ ├── -liquid-dialog-test.js │ │ ├── -liquid-tether-dialog-test.js │ │ ├── -tether-dialog-test.js │ │ └── overlay-test.js │ │ └── modal-dialog-test.js ├── test-helper.js └── unit │ └── services │ └── modal-dialog-test.js └── vendor └── .gitkeep /.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 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /declarations/ 6 | /dist/ 7 | 8 | # misc 9 | /coverage/ 10 | !.* 11 | .*/ 12 | 13 | # ember-try 14 | /.node_modules.ember-try/ 15 | /pnpm-lock.yaml.ember-try 16 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | root: true, 5 | parser: '@babel/eslint-parser', 6 | parserOptions: { 7 | ecmaVersion: 'latest', 8 | sourceType: 'module', 9 | requireConfigFile: false, 10 | babelOptions: { 11 | plugins: [ 12 | [ 13 | '@babel/plugin-proposal-decorators', 14 | { 15 | decoratorsBeforeExport: true, 16 | }, 17 | ], 18 | ], 19 | }, 20 | }, 21 | plugins: ['ember'], 22 | extends: [ 23 | 'eslint:recommended', 24 | 'plugin:ember/recommended', 25 | 'plugin:prettier/recommended', 26 | ], 27 | env: { 28 | browser: true, 29 | }, 30 | rules: { 31 | 'no-setter-return': 'off', 32 | 'ember/classic-decorator-hooks': 'warn', 33 | 'ember/no-classic-classes': 'warn', 34 | 'ember/no-classic-components': 'warn', 35 | 'ember/no-component-lifecycle-hooks': 'warn', 36 | 'ember/no-computed-properties-in-native-classes': 'warn', 37 | 'ember/no-observers': 'warn', 38 | 'ember/require-tagless-components': 'warn', 39 | }, 40 | overrides: [ 41 | // node files 42 | { 43 | files: [ 44 | './.eslintrc.js', 45 | './.prettierrc.js', 46 | './.stylelintrc.js', 47 | './.template-lintrc.js', 48 | './ember-cli-build.js', 49 | './index.js', 50 | './testem.js', 51 | './blueprints/*/index.js', 52 | './config/**/*.js', 53 | './tests/dummy/config/**/*.js', 54 | ], 55 | parserOptions: { 56 | sourceType: 'script', 57 | }, 58 | env: { 59 | browser: false, 60 | node: true, 61 | }, 62 | extends: ['plugin:n/recommended'], 63 | }, 64 | { 65 | // test files 66 | files: ['tests/**/*-test.{js,ts}'], 67 | extends: ['plugin:qunit/recommended'], 68 | }, 69 | ], 70 | }; 71 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | env: 4 | VOLTA_FEATURE_PNPM: 1 5 | 6 | on: 7 | push: 8 | branches: 9 | - main 10 | - master 11 | pull_request: {} 12 | 13 | concurrency: 14 | group: ci-${{ github.head_ref || github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | test: 19 | name: "Tests" 20 | runs-on: ubuntu-latest 21 | timeout-minutes: 5 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Install volta 26 | uses: volta-cli/action@v4 27 | - name: Get pnpm store directory 28 | id: pnpm-cache 29 | shell: bash 30 | run: | 31 | echo "STORE_PATH=$(pnpm store path -s)" >> $GITHUB_OUTPUT 32 | - name: Setup pnpm cache 33 | uses: actions/cache@v4 34 | id: cache 35 | with: 36 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 37 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 38 | restore-keys: | 39 | ${{ runner.os }}-pnpm-store- 40 | - name: Install Dependencies 41 | shell: bash 42 | run: pnpm install --frozen-lockfile 43 | - name: Prune pnpm store 44 | if: ${{ steps.cache.outputs.cache-hit != 'true' }} 45 | shell: bash 46 | run: pnpm store prune 47 | - name: Lint 48 | run: pnpm lint 49 | - name: Run Tests 50 | run: pnpm test 51 | 52 | floating: 53 | name: "Floating Dependencies" 54 | runs-on: ubuntu-latest 55 | timeout-minutes: 5 56 | 57 | steps: 58 | - uses: actions/checkout@v4 59 | - name: Install volta 60 | uses: volta-cli/action@v4 61 | - name: Get pnpm store directory 62 | id: pnpm-cache 63 | shell: bash 64 | run: | 65 | echo "STORE_PATH=$(pnpm store path -s)" >> $GITHUB_OUTPUT 66 | - name: Setup pnpm cache 67 | uses: actions/cache@v4 68 | id: cache 69 | with: 70 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 71 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 72 | restore-keys: | 73 | ${{ runner.os }}-pnpm-store- 74 | - name: Install Dependencies 75 | shell: bash 76 | run: pnpm install --no-frozen-lockfile 77 | - name: Prune pnpm store 78 | if: ${{ steps.cache.outputs.cache-hit != 'true' }} 79 | shell: bash 80 | run: pnpm store prune 81 | - name: Run Tests 82 | run: pnpm test 83 | 84 | try-scenarios: 85 | name: ${{ matrix.try-scenario }} 86 | runs-on: ubuntu-latest 87 | timeout-minutes: 5 88 | 89 | strategy: 90 | fail-fast: false 91 | matrix: 92 | try-scenario: 93 | - ember-lts-3.28 94 | - ember-lts-4.12 95 | - ember-lts-5.12 96 | - ember-release 97 | - ember-beta 98 | - ember-canary 99 | - embroider-safe 100 | # - embroider-optimized 101 | 102 | steps: 103 | - uses: actions/checkout@v4 104 | - name: Install volta 105 | uses: volta-cli/action@v4 106 | - name: Get pnpm store directory 107 | id: pnpm-cache 108 | shell: bash 109 | run: | 110 | echo "STORE_PATH=$(pnpm store path -s)" >> $GITHUB_OUTPUT 111 | - name: Setup pnpm cache 112 | uses: actions/cache@v4 113 | id: cache 114 | with: 115 | path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} 116 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} 117 | restore-keys: | 118 | ${{ runner.os }}-pnpm-store- 119 | - name: Install Dependencies 120 | shell: bash 121 | run: pnpm install --frozen-lockfile 122 | - name: Prune pnpm store 123 | if: ${{ steps.cache.outputs.cache-hit != 'true' }} 124 | shell: bash 125 | run: pnpm store prune 126 | - name: Run Tests 127 | run: pnpm test:ember-compatibility ${{ matrix.try-scenario }} --- pnpm test 128 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /declarations/ 4 | 5 | # dependencies 6 | /node_modules/ 7 | 8 | # misc 9 | /.env* 10 | /.pnp* 11 | /.eslintcache 12 | /coverage/ 13 | /npm-debug.log* 14 | /testem.log 15 | /yarn-error.log 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /npm-shrinkwrap.json.ember-try 20 | /package.json.ember-try 21 | /package-lock.json.ember-try 22 | /yarn.lock.ember-try 23 | /pnpm-lock.yaml.ember-try 24 | 25 | # broccoli-debug 26 | /DEBUG/ 27 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # misc 6 | /.editorconfig 7 | /.ember-cli 8 | /.env* 9 | /.eslintcache 10 | /.eslintignore 11 | /.eslintrc.js 12 | /.git/ 13 | /.github/ 14 | /.gitignore 15 | /.prettierignore 16 | /.prettierrc.js 17 | /.stylelintignore 18 | /.stylelintrc.js 19 | /.template-lintrc.js 20 | /.watchmanconfig 21 | /CONTRIBUTING.md 22 | /ember-cli-build.js 23 | /testem.js 24 | /tests/ 25 | /tsconfig.declarations.json 26 | /tsconfig.json 27 | /yarn-error.log 28 | /yarn.lock 29 | /pnpm-lock.yaml 30 | .gitkeep 31 | 32 | # ember-try 33 | /.node_modules.ember-try/ 34 | /npm-shrinkwrap.json.ember-try 35 | /package.json.ember-try 36 | /package-lock.json.ember-try 37 | /yarn.lock.ember-try 38 | /pnpm-lock.yaml.ember-try 39 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | 7 | # misc 8 | /coverage/ 9 | !.* 10 | .*/ 11 | 12 | # ember-try 13 | /.node_modules.ember-try/ 14 | /pnpm-lock.yaml.ember-try 15 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | overrides: [ 5 | { 6 | files: '*.hbs', 7 | options: { 8 | printWidth: 64, 9 | singleQuote: false, 10 | }, 11 | }, 12 | { 13 | files: '*.{js,ts}', 14 | options: { 15 | printWidth: 80, 16 | singleQuote: true, 17 | }, 18 | }, 19 | ], 20 | }; 21 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | plugins: ['ember-template-lint-plugin-prettier'], 5 | extends: ['recommended', 'ember-template-lint-plugin-prettier:recommended'], 6 | rules: { 7 | 'no-action': false, 8 | }, 9 | overrides: [ 10 | { 11 | files: ['tests/**/*-test.{js,ts}'], 12 | rules: { 13 | prettier: 'off', 14 | }, 15 | }, 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v4.1.5 (2024-12-07) 2 | 3 | #### :bug: Bug Fix 4 | * [#404](https://github.com/yapplabs/ember-modal-dialog/pull/404) Move items from devDeps to peerDeps and update peerDepsMeta to clarify dependencies ([@lukemelia](https://github.com/lukemelia)) 5 | 6 | #### :memo: Documentation 7 | * [#403](https://github.com/yapplabs/ember-modal-dialog/pull/403) Adopt pnpm ([@lukemelia](https://github.com/lukemelia)) 8 | 9 | #### :house: Internal 10 | * [#406](https://github.com/yapplabs/ember-modal-dialog/pull/406) Convert ModalDialog component to glimmer ([@lukemelia](https://github.com/lukemelia)) 11 | * [#405](https://github.com/yapplabs/ember-modal-dialog/pull/405) Move Overlay component to Glimmer ([@lukemelia](https://github.com/lukemelia)) 12 | * [#403](https://github.com/yapplabs/ember-modal-dialog/pull/403) Adopt pnpm ([@lukemelia](https://github.com/lukemelia)) 13 | * [#408](https://github.com/yapplabs/ember-modal-dialog/pull/408) Updated dependencies and ember-try scenarios ([@ijlee2](https://github.com/ijlee2)) 14 | * [#409](https://github.com/yapplabs/ember-modal-dialog/pull/409) Removed classic layout and ember-decorators ([@ijlee2](https://github.com/ijlee2)) 15 | * [#410](https://github.com/yapplabs/ember-modal-dialog/pull/410) Removed {{action}} helper from docs-app ([@ijlee2](https://github.com/ijlee2)) 16 | * [#411](https://github.com/yapplabs/ember-modal-dialog/pull/411) Removed {{action}} helper from addon ([@ijlee2](https://github.com/ijlee2)) 17 | 18 | #### Committers: 2 19 | - Isaac Lee ([@ijlee2](https://github.com/ijlee2)) 20 | - Luke Melia ([@lukemelia](https://github.com/lukemelia)) 21 | 22 | ## v4.1.4 (2024-06-28) 23 | 24 | #### :bug: Bug Fix 25 | * [#400](https://github.com/yapplabs/ember-modal-dialog/pull/400) Avoid Fastboot crash when accessing `navigator` global ([@gilest](https://github.com/gilest)) 26 | 27 | #### Committers: 1 28 | - Giles Thompson ([@gilest](https://github.com/gilest)) 29 | 30 | ## v4.1.3 (2024-01-04) 31 | 32 | ## v4.1.2 (2023-05-08) 33 | 34 | #### :bug: Bug Fix 35 | * [#395](https://github.com/yapplabs/ember-modal-dialog/pull/395) ember-tether version change was missing in peerDependencies declaration ([@lukemelia](https://github.com/lukemelia)) 36 | 37 | #### Committers: 1 38 | - Luke Melia ([@lukemelia](https://github.com/lukemelia)) 39 | 40 | ## v4.1.1 (2023-05-05) 41 | 42 | ## v4.1.0 (2023-02-23) 43 | 44 | #### :rocket: Enhancement 45 | * [#389](https://github.com/yapplabs/ember-modal-dialog/pull/389) Add splattibutes support and fix containerClassNames for rendered in place modals ([@ratierd](https://github.com/ratierd)) 46 | 47 | #### :bug: Bug Fix 48 | * [#389](https://github.com/yapplabs/ember-modal-dialog/pull/389) Add splattibutes support and fix containerClassNames for rendered in place modals ([@ratierd](https://github.com/ratierd)) 49 | 50 | #### :house: Internal 51 | * [#390](https://github.com/yapplabs/ember-modal-dialog/pull/390) Run tests with node 16 ([@lukemelia](https://github.com/lukemelia)) 52 | 53 | #### Committers: 2 54 | - David Ratier ([@ratierd](https://github.com/ratierd)) 55 | - Luke Melia ([@lukemelia](https://github.com/lukemelia)) 56 | 57 | ## v4.0.1 (2022-04-10) 58 | 59 | #### :bug: Bug Fix 60 | * [#378](https://github.com/yapplabs/ember-modal-dialog/pull/378) Fix error attempting to join class names when they are a string ([@lukemelia](https://github.com/lukemelia)) 61 | 62 | #### Committers: 2 63 | - Luke Melia ([@lukemelia](https://github.com/lukemelia)) 64 | - Robert Wagner ([@rwwagner90](https://github.com/rwwagner90)) 65 | 66 | ## v4.0.0 (2022-02-09) 67 | 68 | #### :boom: Breaking Change 69 | 70 | Drops support for Ember 3.19 and below 71 | 72 | #### :rocket: Enhancement 73 | 74 | Ember 4 compatibility and Embroider support 75 | 76 | ## v4.0.0-beta.0 (2022-01-24) 77 | 78 | #### :rocket: Enhancement 79 | * [#358](https://github.com/yapplabs/ember-modal-dialog/pull/358) Start on embroider support ([@rwwagner90](https://github.com/rwwagner90)) 80 | 81 | #### :house: Internal 82 | * [#370](https://github.com/yapplabs/ember-modal-dialog/pull/370) Embroider support ([@rwwagner90](https://github.com/rwwagner90)) 83 | * [#369](https://github.com/yapplabs/ember-modal-dialog/pull/369) Bump deps, yarn upgrade ([@rwwagner90](https://github.com/rwwagner90)) 84 | * [#365](https://github.com/yapplabs/ember-modal-dialog/pull/365) ember-cli 4 ([@rwwagner90](https://github.com/rwwagner90)) 85 | * [#361](https://github.com/yapplabs/ember-modal-dialog/pull/361) Remove ember-classic-decorator ([@rwwagner90](https://github.com/rwwagner90)) 86 | 87 | #### Committers: 1 88 | - Robert Wagner ([@rwwagner90](https://github.com/rwwagner90)) 89 | 90 | ## v4.0.0-alpha.1 (2021-09-09) 91 | 92 | #### :boom: Breaking Change 93 | * [#350](https://github.com/yapplabs/ember-modal-dialog/pull/350) Stop delegating targetAttachmentClass argument ([@lukemelia](https://github.com/lukemelia)) 94 | 95 | #### :house: Internal 96 | * [#351](https://github.com/yapplabs/ember-modal-dialog/pull/351) Avoid global `Ember` deprecation in add-modals-container initializer ([@lukemelia](https://github.com/lukemelia)) 97 | * [#346](https://github.com/yapplabs/ember-modal-dialog/pull/346) Update lint commands ([@rwwagner90](https://github.com/rwwagner90)) 98 | * [#345](https://github.com/yapplabs/ember-modal-dialog/pull/345) Use GitHub Actions instead of Travis ([@rwwagner90](https://github.com/rwwagner90)) 99 | * [#344](https://github.com/yapplabs/ember-modal-dialog/pull/344) Update to angle bracket components ([@rwwagner90](https://github.com/rwwagner90)) 100 | * [#341](https://github.com/yapplabs/ember-modal-dialog/pull/341) Update to ember-cli 3.16 ([@rwwagner90](https://github.com/rwwagner90)) 101 | * [#338](https://github.com/yapplabs/ember-modal-dialog/pull/338) Adopt ember-ignore-children-helper ([@bjornharrtell](https://github.com/bjornharrtell)) 102 | 103 | #### Committers: 3 104 | - Björn Harrtell ([@bjornharrtell](https://github.com/bjornharrtell)) 105 | - Luke Melia ([@lukemelia](https://github.com/lukemelia)) 106 | - Robert Wagner ([@rwwagner90](https://github.com/rwwagner90)) 107 | 108 | ## v4.0.0-alpha.0 (2021-05-25) 109 | 110 | #### :boom: Breaking Change 111 | * [#332](https://github.com/yapplabs/ember-modal-dialog/pull/332) Drop support for Ember 3.19 and below ([@Turbo87](https://github.com/Turbo87)) 112 | 113 | #### :house: Internal 114 | * [#333](https://github.com/yapplabs/ember-modal-dialog/pull/333) Add volta config ([@lukemelia](https://github.com/lukemelia)) 115 | * [#324](https://github.com/yapplabs/ember-modal-dialog/pull/324) Upgrade `ember-wormhole` to v0.6.0 ([@legallai](https://github.com/legallai)) 116 | * [#331](https://github.com/yapplabs/ember-modal-dialog/pull/331) Remove obsolete `this.get(...)` calls ([@Turbo87](https://github.com/Turbo87)) 117 | * [#330](https://github.com/yapplabs/ember-modal-dialog/pull/330) dummy: Remove obsolete `register-subclassed-modals` initializer ([@Turbo87](https://github.com/Turbo87)) 118 | * [#329](https://github.com/yapplabs/ember-modal-dialog/pull/329) Add missing `this.` prefixes to the templates ([@Turbo87](https://github.com/Turbo87)) 119 | 120 | #### Committers: 3 121 | - Luke Melia ([@lukemelia](https://github.com/lukemelia)) 122 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 123 | - [@legallai](https://github.com/legallai) 124 | 125 | ## v3.0.3 (2021-05-17) 126 | 127 | #### :bug: Bug Fix 128 | * [#326](https://github.com/yapplabs/ember-modal-dialog/pull/326) initializers/add-modals-container: Work around string injection bug ([@Turbo87](https://github.com/Turbo87)) 129 | 130 | #### :house: Internal 131 | * [#327](https://github.com/yapplabs/ember-modal-dialog/pull/327) Silence deprecation warnings ([@Turbo87](https://github.com/Turbo87)) 132 | 133 | #### Committers: 1 134 | - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) 135 | 136 | ## v3.0.2 (2021-02-18) 137 | 138 | #### :bug: Bug Fix 139 | * [#315](https://github.com/yapplabs/ember-modal-dialog/pull/315) fix: support `ember-engines` ([@buschtoens](https://github.com/buschtoens)) 140 | 141 | #### Committers: 1 142 | - Jan Buschtöns ([@buschtoens](https://github.com/buschtoens)) 143 | 144 | ## v3.0.1 (2020-07-24) 145 | 146 | #### :bug: Bug Fix 147 | * [#300](https://github.com/yapplabs/ember-modal-dialog/pull/300) Fix dummy app css and two actions ([@lukemelia](https://github.com/lukemelia)) 148 | * [#295](https://github.com/yapplabs/ember-modal-dialog/pull/295) fixes my previous change to the css installation instructions ([@christophermlne](https://github.com/christophermlne)) 149 | * [#294](https://github.com/yapplabs/ember-modal-dialog/pull/294) fixes bad css import snippet in README installation instructions ([@christophermlne](https://github.com/christophermlne)) 150 | 151 | #### :memo: Documentation 152 | * [#295](https://github.com/yapplabs/ember-modal-dialog/pull/295) fixes my previous change to the css installation instructions ([@christophermlne](https://github.com/christophermlne)) 153 | * [#294](https://github.com/yapplabs/ember-modal-dialog/pull/294) fixes bad css import snippet in README installation instructions ([@christophermlne](https://github.com/christophermlne)) 154 | * [#281](https://github.com/yapplabs/ember-modal-dialog/pull/281) Update README.md ([@newyork-anthonyng](https://github.com/newyork-anthonyng)) 155 | 156 | #### :house: Internal 157 | * [#307](https://github.com/yapplabs/ember-modal-dialog/pull/307) Switch to release-it ([@lukemelia](https://github.com/lukemelia)) 158 | * [#305](https://github.com/yapplabs/ember-modal-dialog/pull/305) Bump websocket-extensions from 0.1.3 to 0.1.4 ([@dependabot[bot]](https://github.com/apps/dependabot)) 159 | * [#304](https://github.com/yapplabs/ember-modal-dialog/pull/304) Bump jquery from 3.4.1 to 3.5.0 ([@dependabot[bot]](https://github.com/apps/dependabot)) 160 | * [#306](https://github.com/yapplabs/ember-modal-dialog/pull/306) Bump lodash from 4.17.11 to 4.17.19 ([@dependabot[bot]](https://github.com/apps/dependabot)) 161 | 162 | #### Committers: 5 163 | - Anthony Ng ([@newyork-anthonyng](https://github.com/newyork-anthonyng)) 164 | - Ben Kiefer ([@benkiefer](https://github.com/benkiefer)) 165 | - Christopher Milne ([@christophermlne](https://github.com/christophermlne)) 166 | - Luke Melia ([@lukemelia](https://github.com/lukemelia)) 167 | - [@mum-never-proud](https://github.com/mum-never-proud) 168 | 169 | # Change Log 170 | 171 | ## [3.0.0-beta.4](https://github.com/yapplabs/ember-modal-dialog/tree/3.0.0-beta.4) (2019-06-18) 172 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v3.0.0-beta.3...3.0.0-beta.4) 173 | 174 | **Merged pull requests:** 175 | 176 | - Pure CSS [\#278](https://github.com/yapplabs/ember-modal-dialog/pull/278) ([chrism](https://github.com/chrism)) 177 | - Upgrading dependencies [\#277](https://github.com/yapplabs/ember-modal-dialog/pull/277) ([chrism](https://github.com/chrism)) 178 | - Move contribution guide to CONTRIBUTING.md [\#274](https://github.com/yapplabs/ember-modal-dialog/pull/274) ([hakilebara](https://github.com/hakilebara)) 179 | - Add a table of contents to README.md [\#272](https://github.com/yapplabs/ember-modal-dialog/pull/272) ([hakilebara](https://github.com/hakilebara)) 180 | 181 | ## [v3.0.0-beta.3](https://github.com/yapplabs/ember-modal-dialog/tree/v3.0.0-beta.3) (2018-11-23) 182 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v3.0.0-beta.2...v3.0.0-beta.3) 183 | 184 | **Merged pull requests:** 185 | 186 | - Only try to invoke onClose action if it has been provided [\#271](https://github.com/yapplabs/ember-modal-dialog/pull/271) ([lukemelia](https://github.com/lukemelia)) 187 | 188 | ## [v3.0.0-beta.2](https://github.com/yapplabs/ember-modal-dialog/tree/v3.0.0-beta.2) (2018-11-12) 189 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v3.0.0-beta.1...v3.0.0-beta.2) 190 | 191 | **Merged pull requests:** 192 | 193 | - \[BREAKING\] Update to ember-cli-babel@7. \(Drops support for ember-cli \< 2.13\) [\#266](https://github.com/yapplabs/ember-modal-dialog/pull/266) ([rwjblue](https://github.com/rwjblue)) 194 | 195 | ## [v3.0.0-beta.1](https://github.com/yapplabs/ember-modal-dialog/tree/v3.0.0-beta.1) (2018-11-12) 196 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v2.4.4...v3.0.0-beta.1) 197 | 198 | **Merged pull requests:** 199 | 200 | - \[BREAKING\] Update ember to 3.5, drop support for Ember 2.4 [\#265](https://github.com/yapplabs/ember-modal-dialog/pull/265) ([lukemelia](https://github.com/lukemelia)) 201 | - use closure actions [\#264](https://github.com/yapplabs/ember-modal-dialog/pull/264) ([mcfiredrill](https://github.com/mcfiredrill)) 202 | - Handle when App.rootElement can be a node, rather than an id [\#263](https://github.com/yapplabs/ember-modal-dialog/pull/263) ([averydev](https://github.com/averydev)) 203 | - update README.md Custom Modals section [\#259](https://github.com/yapplabs/ember-modal-dialog/pull/259) ([hakilebara](https://github.com/hakilebara)) 204 | - Update .travis.yml [\#258](https://github.com/yapplabs/ember-modal-dialog/pull/258) ([samselikoff](https://github.com/samselikoff)) 205 | - Don't sendAction in click handler when component isDestroying or isDestroyed [\#255](https://github.com/yapplabs/ember-modal-dialog/pull/255) ([oscarni](https://github.com/oscarni)) 206 | 207 | ## [v2.4.4](https://github.com/yapplabs/ember-modal-dialog/tree/v2.4.4) (2018-05-14) 208 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v3.0.0-beta.0...v2.4.4) 209 | 210 | ## [v3.0.0-beta.0](https://github.com/yapplabs/ember-modal-dialog/tree/v3.0.0-beta.0) (2018-03-03) 211 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v2.4.3...v3.0.0-beta.0) 212 | 213 | **Merged pull requests:** 214 | 215 | - Update dependencies [\#247](https://github.com/yapplabs/ember-modal-dialog/pull/247) ([lukemelia](https://github.com/lukemelia)) 216 | - \[BREAKING\] Remove deprecations in anticipation of 3.0.0 [\#246](https://github.com/yapplabs/ember-modal-dialog/pull/246) ([lukemelia](https://github.com/lukemelia)) 217 | 218 | ## [v2.4.3](https://github.com/yapplabs/ember-modal-dialog/tree/v2.4.3) (2018-03-03) 219 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v2.4.2...v2.4.3) 220 | 221 | **Merged pull requests:** 222 | 223 | - Fix bug in specifying incorrect overlay selector on iOS [\#245](https://github.com/yapplabs/ember-modal-dialog/pull/245) ([lukemelia](https://github.com/lukemelia)) 224 | 225 | ## [v2.4.2](https://github.com/yapplabs/ember-modal-dialog/tree/v2.4.2) (2018-02-22) 226 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v2.4.1...v2.4.2) 227 | 228 | **Merged pull requests:** 229 | 230 | - Update version compatibility \(which controls ember:try version targets\) [\#243](https://github.com/yapplabs/ember-modal-dialog/pull/243) ([lukemelia](https://github.com/lukemelia)) 231 | - Fix bug with clickOutsideToClose when specifying a stack [\#242](https://github.com/yapplabs/ember-modal-dialog/pull/242) ([andrewhavens](https://github.com/andrewhavens)) 232 | - ember-cli-update to 2.17 [\#235](https://github.com/yapplabs/ember-modal-dialog/pull/235) ([Dhaulagiri](https://github.com/Dhaulagiri)) 233 | - Removing jQuery [\#234](https://github.com/yapplabs/ember-modal-dialog/pull/234) ([gmurphey](https://github.com/gmurphey)) 234 | - Confusing sentence [\#223](https://github.com/yapplabs/ember-modal-dialog/pull/223) ([dan-ste](https://github.com/dan-ste)) 235 | 236 | ## [v2.4.1](https://github.com/yapplabs/ember-modal-dialog/tree/v2.4.1) (2017-12-05) 237 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v2.4.0...v2.4.1) 238 | 239 | **Merged pull requests:** 240 | 241 | - Revert modules update to file in app directory [\#232](https://github.com/yapplabs/ember-modal-dialog/pull/232) ([Dhaulagiri](https://github.com/Dhaulagiri)) 242 | - handle global document being undefined [\#230](https://github.com/yapplabs/ember-modal-dialog/pull/230) ([BryanCrotaz](https://github.com/BryanCrotaz)) 243 | 244 | ## [v2.4.0](https://github.com/yapplabs/ember-modal-dialog/tree/v2.4.0) (2017-11-21) 245 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v2.3.0...v2.4.0) 246 | 247 | **Merged pull requests:** 248 | 249 | - Add ember-cli-version-checker as a dep [\#222](https://github.com/yapplabs/ember-modal-dialog/pull/222) ([samselikoff](https://github.com/samselikoff)) 250 | - Make optional dependencies section easier to read [\#220](https://github.com/yapplabs/ember-modal-dialog/pull/220) ([chrisvdp](https://github.com/chrisvdp)) 251 | - fix\(\#195\): provide default values for concatenatedProperties [\#217](https://github.com/yapplabs/ember-modal-dialog/pull/217) ([RustyToms](https://github.com/RustyToms)) 252 | - use new modules api [\#215](https://github.com/yapplabs/ember-modal-dialog/pull/215) ([Dhaulagiri](https://github.com/Dhaulagiri)) 253 | - Fixed formatting on `ember install ember-tether` [\#209](https://github.com/yapplabs/ember-modal-dialog/pull/209) ([djones](https://github.com/djones)) 254 | - keyboard example - use Ember best practice form instead of .on\(\) [\#208](https://github.com/yapplabs/ember-modal-dialog/pull/208) ([caseywatts](https://github.com/caseywatts)) 255 | - Update imports for newer Ember-cli [\#204](https://github.com/yapplabs/ember-modal-dialog/pull/204) ([mazondo](https://github.com/mazondo)) 256 | 257 | ## [v2.3.0](https://github.com/yapplabs/ember-modal-dialog/tree/v2.3.0) (2017-06-23) 258 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v2.2.0...v2.3.0) 259 | 260 | **Merged pull requests:** 261 | 262 | - Don't fail if ENV\['ember-modal-dialog'\] is not defined [\#202](https://github.com/yapplabs/ember-modal-dialog/pull/202) ([lukemelia](https://github.com/lukemelia)) 263 | - Pass `stack` and `value` properties through to liquid-wormhole/liquid-tether [\#201](https://github.com/yapplabs/ember-modal-dialog/pull/201) ([lukemelia](https://github.com/lukemelia)) 264 | - Fix modals in FastBoot [\#200](https://github.com/yapplabs/ember-modal-dialog/pull/200) ([sandydoo](https://github.com/sandydoo)) 265 | - Typo: missing comma [\#197](https://github.com/yapplabs/ember-modal-dialog/pull/197) ([jacobq](https://github.com/jacobq)) 266 | 267 | ## [v2.2.0](https://github.com/yapplabs/ember-modal-dialog/tree/v2.2.0) (2017-05-15) 268 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v2.1.0...v2.2.0) 269 | 270 | **Merged pull requests:** 271 | 272 | - Make modal-dialog component animatable via liquid-fire [\#193](https://github.com/yapplabs/ember-modal-dialog/pull/193) ([lukemelia](https://github.com/lukemelia)) 273 | - Add support for an `overlayPosition` property which supports values of `'parent'` or `'sibling'` [\#192](https://github.com/yapplabs/ember-modal-dialog/pull/192) ([lukemelia](https://github.com/lukemelia)) 274 | - Allow specifying `hasOverlay=false` to suppress output of the overlay div. [\#191](https://github.com/yapplabs/ember-modal-dialog/pull/191) ([lukemelia](https://github.com/lukemelia)) 275 | - Minor README cleanup [\#190](https://github.com/yapplabs/ember-modal-dialog/pull/190) ([lukemelia](https://github.com/lukemelia)) 276 | 277 | ## [v2.1.0](https://github.com/yapplabs/ember-modal-dialog/tree/v2.1.0) (2017-05-14) 278 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v2.0.0...v2.1.0) 279 | 280 | **Merged pull requests:** 281 | 282 | - Detect ember-tether and throw an error early if not present and tetherTarget is passed. [\#189](https://github.com/yapplabs/ember-modal-dialog/pull/189) ([lukemelia](https://github.com/lukemelia)) 283 | - Make tests more robust with waitUntil [\#188](https://github.com/yapplabs/ember-modal-dialog/pull/188) ([lukemelia](https://github.com/lukemelia)) 284 | - Simplify dummy app a bit [\#187](https://github.com/yapplabs/ember-modal-dialog/pull/187) ([lukemelia](https://github.com/lukemelia)) 285 | - Unify `modal-dialog` and `tether-dialog` into one `modal-dialog` component [\#186](https://github.com/yapplabs/ember-modal-dialog/pull/186) ([lukemelia](https://github.com/lukemelia)) 286 | - Replace `modal-dialog-overlay` component with a div and deprecate it. [\#185](https://github.com/yapplabs/ember-modal-dialog/pull/185) ([lukemelia](https://github.com/lukemelia)) 287 | - Update title of dummy app [\#184](https://github.com/yapplabs/ember-modal-dialog/pull/184) ([lukemelia](https://github.com/lukemelia)) 288 | - Rename `close` action to `onClose` and deprecate `close`. [\#183](https://github.com/yapplabs/ember-modal-dialog/pull/183) ([lukemelia](https://github.com/lukemelia)) 289 | - Switch dummy app/tests to use routes instead of query params [\#182](https://github.com/yapplabs/ember-modal-dialog/pull/182) ([lukemelia](https://github.com/lukemelia)) 290 | - install was installed inadvertently, so let's uninstall the inadvertent install of install [\#181](https://github.com/yapplabs/ember-modal-dialog/pull/181) ([lukemelia](https://github.com/lukemelia)) 291 | - Use ember-native-dom-helpers and async/await [\#180](https://github.com/yapplabs/ember-modal-dialog/pull/180) ([lukemelia](https://github.com/lukemelia)) 292 | - Upgrade ember-cli, ember-cli-babel, and friends [\#179](https://github.com/yapplabs/ember-modal-dialog/pull/179) ([lukemelia](https://github.com/lukemelia)) 293 | - Use camelCase properties, deprecate public kebab case properties [\#178](https://github.com/yapplabs/ember-modal-dialog/pull/178) ([lukemelia](https://github.com/lukemelia)) 294 | 295 | ## [v2.0.0](https://github.com/yapplabs/ember-modal-dialog/tree/v2.0.0) (2017-05-12) 296 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v2.0.0-beta.0...v2.0.0) 297 | 298 | **Merged pull requests:** 299 | 300 | - Update to Babel 6. [\#175](https://github.com/yapplabs/ember-modal-dialog/pull/175) ([rwjblue](https://github.com/rwjblue)) 301 | 302 | ## [v2.0.0-beta.0](https://github.com/yapplabs/ember-modal-dialog/tree/v2.0.0-beta.0) (2017-04-05) 303 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v1.0.0...v2.0.0-beta.0) 304 | 305 | **Merged pull requests:** 306 | 307 | - Update ember-cli and other tooling dependencies. [\#174](https://github.com/yapplabs/ember-modal-dialog/pull/174) ([lukemelia](https://github.com/lukemelia)) 308 | - Update ember-wormhole dependency and ember-tether devDependency [\#173](https://github.com/yapplabs/ember-modal-dialog/pull/173) ([lukemelia](https://github.com/lukemelia)) 309 | - Drop support for Ember versions older than 2.4 [\#172](https://github.com/yapplabs/ember-modal-dialog/pull/172) ([lukemelia](https://github.com/lukemelia)) 310 | 311 | ## [v1.0.0](https://github.com/yapplabs/ember-modal-dialog/tree/v1.0.0) (2017-04-05) 312 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.9.1...v1.0.0) 313 | 314 | **Merged pull requests:** 315 | 316 | - Use Chrome in CI. [\#171](https://github.com/yapplabs/ember-modal-dialog/pull/171) ([lukemelia](https://github.com/lukemelia)) 317 | - \[BREAKING\] Change default scss to use static positioning for render in place. [\#169](https://github.com/yapplabs/ember-modal-dialog/pull/169) ([andrewhavens](https://github.com/andrewhavens)) 318 | - Use yarn for dependency management [\#168](https://github.com/yapplabs/ember-modal-dialog/pull/168) ([andrewhavens](https://github.com/andrewhavens)) 319 | - Add ability to specify a callback that is triggered when overlay is clicked. [\#167](https://github.com/yapplabs/ember-modal-dialog/pull/167) ([andrewhavens](https://github.com/andrewhavens)) 320 | - Check for Ember version with new ember-cli-version-checker API. [\#166](https://github.com/yapplabs/ember-modal-dialog/pull/166) ([dan-ste](https://github.com/dan-ste)) 321 | 322 | ## [v0.9.1](https://github.com/yapplabs/ember-modal-dialog/tree/v0.9.1) (2017-02-14) 323 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.9.0...v0.9.1) 324 | 325 | **Merged pull requests:** 326 | 327 | - Touch event handling on IOS for clickOutsideToClose [\#161](https://github.com/yapplabs/ember-modal-dialog/pull/161) ([Chee7ah](https://github.com/Chee7ah)) 328 | - Add element center to positioned container [\#144](https://github.com/yapplabs/ember-modal-dialog/pull/144) ([wandertosee](https://github.com/wandertosee)) 329 | - Register unique clickOutsideToClose click handlers [\#129](https://github.com/yapplabs/ember-modal-dialog/pull/129) ([oscarni](https://github.com/oscarni)) 330 | 331 | ## [v0.9.0](https://github.com/yapplabs/ember-modal-dialog/tree/v0.9.0) (2016-08-19) 332 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.8...v0.9.0) 333 | 334 | **Merged pull requests:** 335 | 336 | - Support unit and integration tests out of the box. [\#142](https://github.com/yapplabs/ember-modal-dialog/pull/142) ([blimmer](https://github.com/blimmer)) 337 | - Update README to change usage of `ember-key-responder` to `ember-keyboard` [\#141](https://github.com/yapplabs/ember-modal-dialog/pull/141) ([SaladFork](https://github.com/SaladFork)) 338 | - Update ember-cli and use ember try's versionCompatibility [\#136](https://github.com/yapplabs/ember-modal-dialog/pull/136) ([lukemelia](https://github.com/lukemelia)) 339 | 340 | ## [v0.8.8](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.8) (2016-07-13) 341 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.7...v0.8.8) 342 | 343 | **Merged pull requests:** 344 | 345 | - Move back to ember-wormhole 0.3.6 to retain support for older Ember versions [\#135](https://github.com/yapplabs/ember-modal-dialog/pull/135) ([lukemelia](https://github.com/lukemelia)) 346 | 347 | ## [v0.8.7](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.7) (2016-07-12) 348 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.6...v0.8.7) 349 | 350 | **Merged pull requests:** 351 | 352 | - Avoid deprecation warning by updating ember-cli-htmlbars [\#134](https://github.com/yapplabs/ember-modal-dialog/pull/134) ([mdentremont](https://github.com/mdentremont)) 353 | 354 | ## [v0.8.6](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.6) (2016-07-05) 355 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.5...v0.8.6) 356 | 357 | **Merged pull requests:** 358 | 359 | - Bump ember-cli-htmlbars to 1.0.3 [\#123](https://github.com/yapplabs/ember-modal-dialog/pull/123) ([sohara](https://github.com/sohara)) 360 | 361 | ## [v0.8.5](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.5) (2016-06-22) 362 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.4...v0.8.5) 363 | 364 | **Merged pull requests:** 365 | 366 | - Call `this.\_super.init` to avoid ember-cli deprecation [\#132](https://github.com/yapplabs/ember-modal-dialog/pull/132) ([ascot21](https://github.com/ascot21)) 367 | 368 | ## [v0.8.4](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.4) (2016-05-11) 369 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.3...v0.8.4) 370 | 371 | **Merged pull requests:** 372 | 373 | - Improve tether-dialog positioning [\#128](https://github.com/yapplabs/ember-modal-dialog/pull/128) ([chrislopresto](https://github.com/chrislopresto)) 374 | - Update ember-tether version [\#126](https://github.com/yapplabs/ember-modal-dialog/pull/126) ([chrislopresto](https://github.com/chrislopresto)) 375 | - Added link to introduction video [\#118](https://github.com/yapplabs/ember-modal-dialog/pull/118) ([taras](https://github.com/taras)) 376 | - Fixes ember-try dependency for ember 1.13.10 [\#117](https://github.com/yapplabs/ember-modal-dialog/pull/117) ([jeremywrowe](https://github.com/jeremywrowe)) 377 | 378 | ## [v0.8.3](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.3) (2015-12-22) 379 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.2...v0.8.3) 380 | 381 | **Merged pull requests:** 382 | 383 | - Perform a big JSCS suave cleanup [\#110](https://github.com/yapplabs/ember-modal-dialog/pull/110) ([chrislopresto](https://github.com/chrislopresto)) 384 | - Add tether-dialog `constraints` property [\#109](https://github.com/yapplabs/ember-modal-dialog/pull/109) ([chrislopresto](https://github.com/chrislopresto)) 385 | - update suave so tests pass on master [\#108](https://github.com/yapplabs/ember-modal-dialog/pull/108) ([hmcq6](https://github.com/hmcq6)) 386 | - Fixing a small typo. [\#91](https://github.com/yapplabs/ember-modal-dialog/pull/91) ([kiwiupover](https://github.com/kiwiupover)) 387 | 388 | ## [v0.8.2](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.2) (2015-10-26) 389 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.1...v0.8.2) 390 | 391 | **Merged pull requests:** 392 | 393 | - Update to Ember CLI 1.13.8 [\#90](https://github.com/yapplabs/ember-modal-dialog/pull/90) ([chrislopresto](https://github.com/chrislopresto)) 394 | 395 | ## [v0.8.1](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.1) (2015-09-19) 396 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.8.0...v0.8.1) 397 | 398 | **Merged pull requests:** 399 | 400 | - add additional learning resource [\#85](https://github.com/yapplabs/ember-modal-dialog/pull/85) ([jeffreybiles](https://github.com/jeffreybiles)) 401 | - Made the initializer ember 1.13/2.0/2.1 friendly [\#82](https://github.com/yapplabs/ember-modal-dialog/pull/82) ([toranb](https://github.com/toranb)) 402 | 403 | ## [v0.8.0](https://github.com/yapplabs/ember-modal-dialog/tree/v0.8.0) (2015-09-10) 404 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.7.7...v0.8.0) 405 | 406 | **Merged pull requests:** 407 | 408 | - Create separate modal-dialog and tether-dialog components [\#79](https://github.com/yapplabs/ember-modal-dialog/pull/79) ([chrislopresto](https://github.com/chrislopresto)) 409 | - Update css for center scrolling example [\#74](https://github.com/yapplabs/ember-modal-dialog/pull/74) ([samselikoff](https://github.com/samselikoff)) 410 | - Make centered scrolling example work [\#73](https://github.com/yapplabs/ember-modal-dialog/pull/73) ([sandstrom](https://github.com/sandstrom)) 411 | - introduce ember-suave [\#72](https://github.com/yapplabs/ember-modal-dialog/pull/72) ([raycohen](https://github.com/raycohen)) 412 | 413 | ## [v0.7.7](https://github.com/yapplabs/ember-modal-dialog/tree/v0.7.7) (2015-08-04) 414 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.7.6...v0.7.7) 415 | 416 | **Merged pull requests:** 417 | 418 | - refactor `updateAlignment` to make it clear what the different paths are [\#66](https://github.com/yapplabs/ember-modal-dialog/pull/66) ([raycohen](https://github.com/raycohen)) 419 | - assert that specified alignment target exists [\#65](https://github.com/yapplabs/ember-modal-dialog/pull/65) ([kagemusha](https://github.com/kagemusha)) 420 | - Adds the ability to close when clicking anywhere outside the modal without an overlay [\#59](https://github.com/yapplabs/ember-modal-dialog/pull/59) ([kellyselden](https://github.com/kellyselden)) 421 | - Add version check for minimum ember-cli version [\#56](https://github.com/yapplabs/ember-modal-dialog/pull/56) ([lukemelia](https://github.com/lukemelia)) 422 | 423 | ## [v0.7.6](https://github.com/yapplabs/ember-modal-dialog/tree/v0.7.6) (2015-07-27) 424 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.7.5...v0.7.6) 425 | 426 | **Merged pull requests:** 427 | 428 | - Upgrade to ember-cli 1.13.1 [\#63](https://github.com/yapplabs/ember-modal-dialog/pull/63) ([chrislopresto](https://github.com/chrislopresto)) 429 | - Respect MODAL\_DIALOG\_USE\_TETHER application property [\#62](https://github.com/yapplabs/ember-modal-dialog/pull/62) ([chrislopresto](https://github.com/chrislopresto)) 430 | - Generate a changelog [\#61](https://github.com/yapplabs/ember-modal-dialog/pull/61) ([mike-north](https://github.com/mike-north)) 431 | - Make overlay clicks work on iOS automatically by adding `cursor: pointer` to the overlay div. [\#60](https://github.com/yapplabs/ember-modal-dialog/pull/60) ([lukemelia](https://github.com/lukemelia)) 432 | - Add a changelog. [\#54](https://github.com/yapplabs/ember-modal-dialog/pull/54) ([blimmer](https://github.com/blimmer)) 433 | 434 | ## [v0.7.5](https://github.com/yapplabs/ember-modal-dialog/tree/v0.7.5) (2015-06-25) 435 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.7.4...v0.7.5) 436 | 437 | **Merged pull requests:** 438 | 439 | - Pass through additional ember-tether options [\#49](https://github.com/yapplabs/ember-modal-dialog/pull/49) ([chrislopresto](https://github.com/chrislopresto)) 440 | 441 | ## [v0.7.4](https://github.com/yapplabs/ember-modal-dialog/tree/v0.7.4) (2015-06-19) 442 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.7.3...v0.7.4) 443 | 444 | **Merged pull requests:** 445 | 446 | - Allow 2.0.0-beta's to use current templates. [\#44](https://github.com/yapplabs/ember-modal-dialog/pull/44) ([rwjblue](https://github.com/rwjblue)) 447 | 448 | ## [v0.7.3](https://github.com/yapplabs/ember-modal-dialog/tree/v0.7.3) (2015-06-19) 449 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.7.2...v0.7.3) 450 | 451 | **Merged pull requests:** 452 | 453 | - Swap the modal-dialog template based on Ember version. [\#43](https://github.com/yapplabs/ember-modal-dialog/pull/43) ([rwjblue](https://github.com/rwjblue)) 454 | 455 | ## [v0.7.2](https://github.com/yapplabs/ember-modal-dialog/tree/v0.7.2) (2015-05-31) 456 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.7.1...v0.7.2) 457 | 458 | ## [v0.7.1](https://github.com/yapplabs/ember-modal-dialog/tree/v0.7.1) (2015-05-24) 459 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.7.0...v0.7.1) 460 | 461 | **Merged pull requests:** 462 | 463 | - When `renderInPlace` is true, do not use tether or do any positioning [\#36](https://github.com/yapplabs/ember-modal-dialog/pull/36) ([lukemelia](https://github.com/lukemelia)) 464 | 465 | ## [v0.7.0](https://github.com/yapplabs/ember-modal-dialog/tree/v0.7.0) (2015-05-19) 466 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.6.0...v0.7.0) 467 | 468 | **Merged pull requests:** 469 | 470 | - Use ember-tether for modal positioning [\#35](https://github.com/yapplabs/ember-modal-dialog/pull/35) ([chrislopresto](https://github.com/chrislopresto)) 471 | - Clarifies the close action's functionality in the documentation [\#32](https://github.com/yapplabs/ember-modal-dialog/pull/32) ([pzuraq](https://github.com/pzuraq)) 472 | - Guard against `document` being undefined. [\#31](https://github.com/yapplabs/ember-modal-dialog/pull/31) ([rwjblue](https://github.com/rwjblue)) 473 | - Remove public [\#30](https://github.com/yapplabs/ember-modal-dialog/pull/30) ([sandstrom](https://github.com/sandstrom)) 474 | 475 | ## [v0.6.0](https://github.com/yapplabs/ember-modal-dialog/tree/v0.6.0) (2015-05-05) 476 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.5.0...v0.6.0) 477 | 478 | **Merged pull requests:** 479 | 480 | - Add modal-dialog service with `destinationElementId` for easier subclassing [\#28](https://github.com/yapplabs/ember-modal-dialog/pull/28) ([rlivsey](https://github.com/rlivsey)) 481 | - Improve routable usage info. [\#27](https://github.com/yapplabs/ember-modal-dialog/pull/27) ([pedrokiefer](https://github.com/pedrokiefer)) 482 | - Component unit test example [\#20](https://github.com/yapplabs/ember-modal-dialog/pull/20) ([varblob](https://github.com/varblob)) 483 | 484 | ## [v0.5.0](https://github.com/yapplabs/ember-modal-dialog/tree/v0.5.0) (2015-04-28) 485 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.4.1...v0.5.0) 486 | 487 | **Merged pull requests:** 488 | 489 | - Wormhole-less Modals [\#26](https://github.com/yapplabs/ember-modal-dialog/pull/26) ([chrislopresto](https://github.com/chrislopresto)) 490 | - Add an alignment css class to the positioned div [\#25](https://github.com/yapplabs/ember-modal-dialog/pull/25) ([lukemelia](https://github.com/lukemelia)) 491 | - Fix typo: modals-overlay =\> modal-overlays [\#23](https://github.com/yapplabs/ember-modal-dialog/pull/23) ([balinterdi](https://github.com/balinterdi)) 492 | - Update install instructions for Ember CLI 0.2.3 [\#21](https://github.com/yapplabs/ember-modal-dialog/pull/21) ([balinterdi](https://github.com/balinterdi)) 493 | 494 | ## [v0.4.1](https://github.com/yapplabs/ember-modal-dialog/tree/v0.4.1) (2015-04-25) 495 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.4.0...v0.4.1) 496 | 497 | ## [v0.4.0](https://github.com/yapplabs/ember-modal-dialog/tree/v0.4.0) (2015-04-17) 498 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.3.0...v0.4.0) 499 | 500 | ## [v0.3.0](https://github.com/yapplabs/ember-modal-dialog/tree/v0.3.0) (2015-04-13) 501 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.2.2...v0.3.0) 502 | 503 | **Merged pull requests:** 504 | 505 | - Introduced overlay-class and container-class attributes. [\#18](https://github.com/yapplabs/ember-modal-dialog/pull/18) ([lukemelia](https://github.com/lukemelia)) 506 | - Allow alignmentTarget to be specified as a reference to an Ember View or a DOM element in addition to a selector. [\#16](https://github.com/yapplabs/ember-modal-dialog/pull/16) ([lukemelia](https://github.com/lukemelia)) 507 | 508 | ## [v0.2.2](https://github.com/yapplabs/ember-modal-dialog/tree/v0.2.2) (2015-04-11) 509 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/v0.2.1...v0.2.2) 510 | 511 | ## [v0.2.1](https://github.com/yapplabs/ember-modal-dialog/tree/v0.2.1) (2015-04-10) 512 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/0.2.0...v0.2.1) 513 | 514 | **Merged pull requests:** 515 | 516 | - Remove orphan overlay component [\#13](https://github.com/yapplabs/ember-modal-dialog/pull/13) ([olivia](https://github.com/olivia)) 517 | - Updating ember-cli version to fix build errors on windows [\#12](https://github.com/yapplabs/ember-modal-dialog/pull/12) ([olivia](https://github.com/olivia)) 518 | - Clarify key-responder is about keyboard shortcuts [\#10](https://github.com/yapplabs/ember-modal-dialog/pull/10) ([samselikoff](https://github.com/samselikoff)) 519 | 520 | ## [0.2.0](https://github.com/yapplabs/ember-modal-dialog/tree/0.2.0) (2015-04-05) 521 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/0.1.0...0.2.0) 522 | 523 | **Merged pull requests:** 524 | 525 | - Test against 1.10 through beta [\#7](https://github.com/yapplabs/ember-modal-dialog/pull/7) ([ef4](https://github.com/ef4)) 526 | - Drop unused ember-data dependency [\#6](https://github.com/yapplabs/ember-modal-dialog/pull/6) ([ef4](https://github.com/ef4)) 527 | - Use Ember.observer and Ember.on instead of function prototype equivalents [\#4](https://github.com/yapplabs/ember-modal-dialog/pull/4) ([lukemelia](https://github.com/lukemelia)) 528 | 529 | ## [0.1.0](https://github.com/yapplabs/ember-modal-dialog/tree/0.1.0) (2015-03-26) 530 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/0.0.3...0.1.0) 531 | 532 | **Merged pull requests:** 533 | 534 | - Metal Modal Approach [\#2](https://github.com/yapplabs/ember-modal-dialog/pull/2) ([chrislopresto](https://github.com/chrislopresto)) 535 | 536 | ## [0.0.3](https://github.com/yapplabs/ember-modal-dialog/tree/0.0.3) (2014-11-14) 537 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/0.0.2...0.0.3) 538 | 539 | ## [0.0.2](https://github.com/yapplabs/ember-modal-dialog/tree/0.0.2) (2014-10-12) 540 | [Full Changelog](https://github.com/yapplabs/ember-modal-dialog/compare/0.0.1...0.0.2) 541 | 542 | ## [0.0.1](https://github.com/yapplabs/ember-modal-dialog/tree/0.0.1) (2014-10-05) 543 | 544 | 545 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* 546 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | ## Installation 4 | 5 | * `git clone ` 6 | * `cd my-addon` 7 | * `npm install` 8 | 9 | ## Linting 10 | 11 | * `npm run lint:hbs` 12 | * `npm run lint:js` 13 | * `npm run lint:js -- --fix` 14 | 15 | ## Running tests 16 | 17 | * `ember test` – Runs the test suite on the current Ember version 18 | * `ember test --server` – Runs the test suite in "watch mode" 19 | * `ember try:each` – Runs the test suite against multiple Ember versions 20 | 21 | ### Unit Tests 22 | 23 | When running unit tests on components that use ember-modal-dialog, modals will be 24 | attached to the `#ember-testing` div. 25 | 26 | 27 | ## Running the Dummy Application 28 | 29 | * `ember serve` 30 | * Visit the dummy application at [http://localhost:4200](http://localhost:4200). 31 | 32 | For more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/). 33 | 34 | ## Generating the Changelog 35 | 36 | This project uses [https://github.com/skywinder/github-changelog-generator](https://github.com/skywinder/github-changelog-generator) to generate its changelog. 37 | 38 | `github_changelog_generator --future-release=x.y.z --no-issues` 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ember Modal Dialog [![Build Status](https://github.com/yapplabs/ember-modal-dialog/actions/workflows/ci.yml/badge.svg)](https://github.com/yapplabs/ember-modal-dialog/actions/workflows/ci.yml) [![Ember Observer Score](http://emberobserver.com/badges/ember-modal-dialog.svg)](http://emberobserver.com/addons/ember-modal-dialog) 2 | 3 | ## Regarding the Status of this Addon 4 | 5 | A lot has happened in browser APIs, the JS ecosytem, and in the Ember ecosystem since this addon was created. 6 | 7 | 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. 8 | 9 | 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. 10 | 11 | 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. 12 | 13 | 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. 14 | 15 | ## Overview 16 | 17 | The ember-modal-dialog addon provides components to implement modal dialogs throughout an Ember application using a simple, consistent pattern. 18 | 19 | Unlike some other modal libraries for Ember, ember-modal-dialog uses solutions like [ember-wormhole](//github.com/yapplabs/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. 20 | 21 | ## Table of Contents 22 | 23 | 24 | 25 | - [Ember Modal Dialog ](#ember-modal-dialog--) 26 | - [Table of Contents](#table-of-contents) 27 | - [Live Demo and Test Examples](#live-demo-and-test-examples) 28 | - [Including In An Ember Application](#including-in-an-ember-application) 29 | - [Upgrading](#upgrading) 30 | - [Controller-bound Usage](#controller-bound-usage) 31 | - [Routable Usage](#routable-usage) 32 | - [Configurable Properties](#configurable-properties) 33 | - [modal-dialog](#modal-dialog) 34 | - [Tethering](#tethering) 35 | - [Animation](#animation) 36 | - [Optional Dependencies](#optional-dependencies) 37 | - [Which Component Should I Use?](#which-component-should-i-use) 38 | - [Positioning](#positioning) 39 | - [Caveats](#caveats) 40 | - [Wormholes](#wormholes) 41 | - [Configuring the Modal Root Element Id](#configuring-the-modal-root-element-id) 42 | - [Configuring Styles](#configuring-styles) 43 | - [Keyboard shortcuts](#keyboard-shortcuts) 44 | - [iOS](#ios) 45 | - [Custom Modals](#custom-modals) 46 | - [Using as a nested addon](#using-as-a-nested-addon) 47 | - [Compatibility \& Dependencies](#compatibility--dependencies) 48 | - [Additional Resources](#additional-resources) 49 | - [Contributing](#contributing) 50 | - [Credits](#credits) 51 | 52 | 53 | 54 | ## Live Demo and Test Examples 55 | 56 | View a live demo here: [http://yapplabs.github.io/ember-modal-dialog/](http://yapplabs.github.io/ember-modal-dialog/) 57 | 58 | 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. 59 | 60 | [![Video image](https://i.vimeocdn.com/video/558401687_640x360.jpg)](https://vimeo.com/157192323) 61 | 62 | ## Including In An Ember Application 63 | 64 | Here is the simplest way to get started with ember-modal-dialog: 65 | 66 | ```sh 67 | ember install ember-modal-dialog 68 | ``` 69 | 70 | Then import the CSS files 71 | 72 | **app.css** 73 | ```css 74 | @import "ember-modal-dialog/ember-modal-structure.css"; 75 | @import "ember-modal-dialog/ember-modal-appearance.css"; 76 | ``` 77 | 78 | If you’re using SASS then just import the CSS slightly differently 79 | 80 | **app.scss** 81 | ```scss 82 | @import "ember-modal-dialog/ember-modal-structure"; 83 | @import "ember-modal-dialog/ember-modal-appearance"; 84 | ``` 85 | 86 | **application.hbs** 87 | ```htmlbars 88 | {{#modal-dialog}} 89 | Oh hai there! 90 | {{/modal-dialog}} 91 | ``` 92 | 93 | ## Upgrading 94 | 95 | Earlier versions of `ember-modal-dialog` required `ember-cli-sass` and an `app.scss` file to import styling. 96 | 97 | Please be aware this is no longer the case. 98 | 99 | 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. 100 | 101 | ## Controller-bound Usage 102 | 103 | Here is a more useful example of how to conditionally display a modal based on a user interaction. 104 | 105 | **Template** 106 | 107 | ```htmlbars 108 | 109 | 110 | {{#if isShowingModal}} 111 | {{#modal-dialog 112 | onClose=(action "toggleModal") 113 | targetAttachment="center" 114 | translucentOverlay=true 115 | }} 116 | Oh hai there! 117 | {{/modal-dialog}} 118 | {{/if}} 119 | ``` 120 | 121 | **Controller** 122 | 123 | ```javascript 124 | import Controller from '@ember/controller'; 125 | 126 | export default Controller.extend({ 127 | isShowingModal: false, 128 | actions: { 129 | toggleModal() { 130 | this.toggleProperty('isShowingModal'); 131 | } 132 | } 133 | }); 134 | ``` 135 | 136 | ## Routable Usage 137 | 138 | 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. 139 | 140 | ## Configurable Properties 141 | 142 | ### modal-dialog 143 | 144 | The modal-dialog component supports the following properties: 145 | 146 | Property | Purpose 147 | --------------------- | ------------- 148 | `hasOverlay` | Toggles presence of overlay div in DOM 149 | `translucentOverlay` | Indicates translucence of overlay, toggles presence of `translucent` CSS selector 150 | `onClose` | The action handler for the dialog's `onClose` action. This action triggers when the user clicks the modal overlay. 151 | `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`. 152 | `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. 153 | `renderInPlace` | A boolean, when true renders the modal without wormholing or tethering, useful for including a modal in a style guide 154 | `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'`) 155 | `containerClass` | CSS class name(s) to append to container divs. Set this from template. 156 | `containerClassNames` | CSS class names to append to container divs. If you subclass this component, you may define this in your subclass.) 157 | `overlayClass` | CSS class name(s) to append to overlay divs. Set this from template. 158 | `overlayClassNames` | CSS class names to append to overlay divs. If you subclass this component, you may define this in your subclass.) 159 | `wrapperClass` | CSS class name(s) to append to wrapper divs. Set this from template. 160 | `wrapperClassNames` | CSS class names to append to wrapper divs. If you subclass this component, you may define this in your subclass.) 161 | `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.) 162 | 163 | #### Tethering 164 | 165 | If you specify a `tetherTarget`, you are opting into "tethering" behavior, and you must have either `ember-tether` or `liquid-tether` installed. 166 | 167 | Property | Purpose 168 | --------------------- | ------------- 169 | `tetherTarget` | Element selector or element reference for that serves as the reference for modal position 170 | 171 | We use the amazing [Tether.js](http://tether.io/) library (via [ember-tether](https://github.com/yapplabs/ember-tether)) to let you position your dialog relative to other elements in the DOM. 172 | 173 | \* Please see [Hubspot Tether](http://github.hubspot.com/tether/) for usage documentation. 174 | 175 | When in a tethering scenario, you may also pass the following properties, which are passed through to Tether: 176 | 177 | Property | Purpose 178 | --------------------- | ------------- 179 | `attachment` | Delegates to Hubspot Tether* 180 | `targetAttachment` | Delegates to Hubspot Tether* 181 | `tetherClassPrefix` | Delegates to Hubspot Tether* 182 | `offset` | Delegates to Hubspot Tether* 183 | `targetOffset` | Delegates to Hubspot Tether* 184 | `constraints` | Delegates to Hubspot Tether* 185 | 186 | #### Animation 187 | 188 | This component supports animation when certain addons are present (liquid-wormhole, liquid-tether). 189 | 190 | Detection is be automatic. To opt out of using animatable features when you have these `liquid-*` addons installed, pass `animatable=false`. 191 | 192 | When in an animatable scenario, you may also pass the following properties, which are passed through to liquid-wormhole or liquid-tether: 193 | 194 | Property | Purpose 195 | --------------------- | ------------- 196 | `stack` | Delegates to liquid-wormhole/liquid-tether 197 | 198 | ### Optional Dependencies 199 | 200 | Dependency | Documentation 201 | --------------------------------|------------ 202 | `ember install ember-tether` | [Docs](//github.com/yapplabs/ember-tether/) 203 | `ember install liquid-wormhole` | [Docs](//pzuraq.github.io/liquid-wormhole/) 204 | `ember install liquid-tether` | [Docs](//pzuraq.github.io/liquid-tether/) 205 | `ember install liquid-fire` | [Docs](//ember-animation.github.io/liquid-fire/) 206 | 207 | 208 | ## Which Component Should I Use? 209 | 210 | Various modal use cases are best supported by different DOM structures. Ember Modal Dialog's `modal-dialog` component provides the following capabilities: 211 | 212 | - 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 213 | 214 | ![](tests/dummy/public/modal-dialog.png) 215 | 216 | This can be customized (see `overlayPosition`). 217 | 218 | - 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](//github.com/yapplabs/ember-tether) dependency. 219 | 220 | ![](tests/dummy/public/tether-dialog.png) 221 | 222 | ## Positioning 223 | 224 | With the default CSS provided, your modal will be centered in the viewport. By adjusting the CSS, you can adjust this logic. 225 | 226 | 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. 227 | 228 | 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'` 229 | 230 | To enable this behavior, install ember-tether as a dependency of **your ember app**. 231 | 232 | ember install ember-tether 233 | 234 | Then pass a selector as `tetherTarget` for the modal you wish to position this way: 235 | 236 | ```htmlbars 237 | {{#modal-dialog 238 | tetherTarget='#target-element-id' 239 | targetAttachment='middle right' 240 | attachment='middle left' 241 | }} 242 | I am a modal that will remain tethered to the right of the element with id 'target-element-id' 243 | {{/modal-dialog}} 244 | ``` 245 | 246 | #### Caveats 247 | 248 | 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. 249 | 250 | If you are not overriding the default root element, then don't worry and carry on. ember-tether will work just fine for you. 251 | 252 | ## Wormholes 253 | 254 | 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. 255 | 256 | 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. 257 | 258 | 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! 259 | 260 | ## Configuring the Modal Root Element Id 261 | 262 | This default id of the modal root element is `modal-overlays` and can be overridden in environment application options as follows: 263 | 264 | **environment.js** 265 | ```javascript 266 | module.exports = function(environment) { 267 | var ENV = { 268 | // ... 269 | APP: { 270 | // ... 271 | emberModalDialog: { 272 | modalRootElementId: 'custom-modal-root-element' 273 | } 274 | } 275 | }; 276 | // ... 277 | 278 | return ENV; 279 | }; 280 | ``` 281 | 282 | ## Configuring Styles 283 | 284 | You can import the CSS files directly 285 | 286 | **app.css** 287 | ```css 288 | @import "ember-modal-dialog/ember-modal-structure.css"; 289 | @import "ember-modal-dialog/ember-modal-appearance.css"; 290 | ``` 291 | 292 | If you’re using SASS then just import the CSS slightly differently 293 | 294 | **app.scss** 295 | ```scss 296 | @import "ember-modal-dialog/ember-modal-structure"; 297 | @import "ember-modal-dialog/ember-modal-appearance"; 298 | ``` 299 | 300 | ## Keyboard shortcuts 301 | 302 | 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: 303 | 304 | ```js 305 | // app/components/modal-dialog.js 306 | import ModalDialog from 'ember-modal-dialog/components/modal-dialog'; 307 | 308 | const ESC_KEY = 27; 309 | 310 | export default ModalDialog.extend({ 311 | didInsertElement() { 312 | this._super(...arguments); 313 | this._initEscListener(); 314 | }, 315 | 316 | willDestroyElement(){ 317 | this._super(...arguments); 318 | Ember.$('body').off('keyup.modal-dialog'); 319 | }, 320 | 321 | _initEscListener() { 322 | const closeOnEscapeKey = (ev) => { 323 | if (ev.keyCode === ESC_KEY) { get(this, 'onClose')(); } 324 | }; 325 | 326 | Ember.$('body').on('keyup.modal-dialog', closeOnEscapeKey); 327 | }, 328 | }); 329 | ``` 330 | 331 | This can work, but some apps require a more sophisticated approach. One approach takes advantage of the [ember-keyboard](https://github.com/null-null-null/ember-keyboard) library. Here's an example: 332 | 333 | ```javascript 334 | // app/components/modal-dialog.js 335 | import { on } from '@ember/object/evented'; 336 | import ModalDialog from 'ember-modal-dialog/components/modal-dialog'; 337 | import { EKMixin as EmberKeyboardMixin, keyDown } from 'ember-keyboard'; 338 | 339 | export default ModalDialog.extend(EmberKeyboardMixin, { 340 | init() { 341 | this._super(...arguments); 342 | 343 | set(this, 'keyboardActivated', true); 344 | }, 345 | 346 | closeOnEsc: on(keyDown('Escape'), function() { 347 | get(this, 'onClose')(); 348 | }) 349 | }); 350 | ``` 351 | 352 | View [the library](https://github.com/null-null-null/ember-keyboard) for more information. 353 | 354 | ## iOS 355 | 356 | 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`. 357 | 358 | ## Custom Modals 359 | 360 | If you have various different styles of modal dialog in your app, it can be useful to subclass the dialog as a new component: 361 | 362 | ```js 363 | // app/components/full-screen-modal.js 364 | 365 | import ModalDialog from 'ember-modal-dialog/components/modal-dialog'; 366 | 367 | export default ModalDialog.extend({ 368 | containerClassNames: "full-screen-modal", 369 | targetAttachment: "none" 370 | }); 371 | ``` 372 | 373 | 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. 374 | Your component can then be used like so: 375 | 376 | ```htmlbars 377 | {{#full-screen-modal}} 378 | Custom modal contents 379 | {{/full-screen-modal}} 380 | ``` 381 | ## Using as a nested addon 382 | 383 | 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: 384 | 385 | ```js 386 | // index.js 387 | 388 | module.exports = { 389 | name: 'my-addon', 390 | config(environment, appConfig) { 391 | let initialConfig = _.merge({}, appConfig); 392 | let updatedConfig = this.addons.reduce((config, addon) => { 393 | if (addon.config) { 394 | _.merge(config, addon.config(environment, config)); 395 | } 396 | return config; 397 | }, initialConfig); 398 | return updatedConfig; 399 | } 400 | }; 401 | ``` 402 | 403 | ## Compatibility & Dependencies 404 | 405 | * For Ember versions >= 4.0, liquid-tether is not compatible so the animated+tethered variation of ember-modal-dialog will not work 406 | * For Ember versions >= 3.20, use the latest published version 407 | * 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) 408 | * For Ember versions >= 2.4 and < 2.8, use the latest 2.x version 409 | * 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)_ 410 | 411 | ## Additional Resources 412 | 413 | * [Screencast on using ember-modal-dialog](https://www.emberscreencasts.com/posts/74-ember-modal-dialog) 414 | 415 | ## Contributing 416 | 417 | See the [Contributing](CONTRIBUTING.md) guide for details. 418 | 419 | ## Credits 420 | 421 | Contributions from @stefanpenner, @krisselden, @chrislopresto, @lukemelia, @raycohen, @andrewhavens, @samselikoff and 422 | others. [Yapp Labs](http://yapplabs.com) was an Ember.js consultancy based in NYC, that has since been folded into [Yapp](https://www.yapp.us). 423 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release 2 | 3 | Releases are mostly automated using 4 | [release-it](https://github.com/release-it/release-it/) and 5 | [lerna-changelog](https://github.com/lerna/lerna-changelog/). 6 | 7 | 8 | ## Preparation 9 | 10 | Since the majority of the actual release process is automated, the primary 11 | remaining task prior to releasing is confirming that all pull requests that 12 | have been merged since the last release have been labeled with the appropriate 13 | `lerna-changelog` labels and the titles have been updated to ensure they 14 | represent something that would make sense to our users. Some great information 15 | on why this is important can be found at 16 | [keepachangelog.com](https://keepachangelog.com/en/1.0.0/), but the overall 17 | guiding principle here is that changelogs are for humans, not machines. 18 | 19 | When reviewing merged PR's the labels to be used are: 20 | 21 | * breaking - Used when the PR is considered a breaking change. 22 | * enhancement - Used when the PR adds a new feature or enhancement. 23 | * bug - Used when the PR fixes a bug included in a previous release. 24 | * documentation - Used when the PR adds or updates documentation. 25 | * internal - Used for internal changes that still require a mention in the 26 | changelog/release notes. 27 | 28 | 29 | ## Release 30 | 31 | Once the prep work is completed, the actual release is straight forward: 32 | 33 | * First, ensure that you have installed your projects dependencies: 34 | 35 | ``` 36 | pnpm install 37 | ``` 38 | 39 | * Second, ensure that you have obtained a 40 | [GitHub personal access token][generate-token] with the `repo` scope (no 41 | other permissions are needed). Make sure the token is available as the 42 | `GITHUB_AUTH` environment variable. 43 | 44 | For instance: 45 | 46 | ```bash 47 | export GITHUB_AUTH=abc123def456 48 | ``` 49 | 50 | [generate-token]: https://github.com/settings/tokens/new?scopes=repo&description=GITHUB_AUTH+env+variable 51 | 52 | * And last (but not least 😁) do your release. 53 | 54 | ``` 55 | npx release-it 56 | ``` 57 | 58 | [release-it](https://github.com/release-it/release-it/) manages the actual 59 | release process. It will prompt you to to choose the version number after which 60 | you will have the chance to hand tweak the changelog to be used (for the 61 | `CHANGELOG.md` and GitHub release), then `release-it` continues on to tagging, 62 | pushing the tag and commits, etc. 63 | -------------------------------------------------------------------------------- /addon/components/basic-dialog.hbs: -------------------------------------------------------------------------------- 1 | 2 | {{#if this.isOverlaySibling}} 3 |
7 | {{#if this.hasOverlay}} 8 | 12 | {{/if}} 13 | 19 | {{yield}} 20 | 21 |
22 | {{else}} 23 |
27 | {{#if this.hasOverlay}} 28 | 34 | 40 | {{yield}} 41 | 42 | 43 | {{else}} 44 | 50 | {{yield}} 51 | 52 | {{/if}} 53 |
54 | {{/if}} 55 |
-------------------------------------------------------------------------------- /addon/components/basic-dialog.js: -------------------------------------------------------------------------------- 1 | import { computed, set } from '@ember/object'; 2 | import { inject as service } from '@ember/service'; 3 | import Component from '@ember/component'; 4 | import { isEmpty } from '@ember/utils'; 5 | import { dasherize } from '@ember/string'; 6 | 7 | import { isIOS, clickHandlerDelay } from '../utils/config-utils'; 8 | 9 | export default class BasicDialog extends Component { 10 | tagName = ''; 11 | 12 | containerClassNames = null; 13 | overlayClassNames = null; 14 | wrapperClassNames = null; 15 | destinationElementId = null; 16 | translucentOverlay = false; 17 | clickOutsideToClose = false; 18 | hasOverlay = true; 19 | isCentered = true; 20 | overlayPosition = null; 21 | 22 | @service('modal-dialog') 23 | modalService; 24 | 25 | init() { 26 | super.init(...arguments); 27 | if (!this.destinationElementId) { 28 | set(this, 'destinationElementId', this.modalService.destinationElementId); 29 | } 30 | } 31 | 32 | variantWrapperClass = 'emd-static'; 33 | 34 | @computed( 35 | 'attachmentClass', 36 | 'containerClass', 37 | 'containerClassNames.{[],join}', 38 | 'targetAttachmentClass', 39 | ) 40 | get containerClassNamesString() { 41 | let classNames = 42 | (this.containerClassNames?.join && this.containerClassNames?.join(' ')) || 43 | this.containerClassNames; 44 | return [ 45 | 'ember-modal-dialog', 46 | classNames, 47 | this.targetAttachmentClass, 48 | this.attachmentClass, 49 | this.containerClass, 50 | ] 51 | .filter((className) => !isEmpty(className)) 52 | .join(' '); 53 | } 54 | 55 | @computed('overlayClass', 'overlayClassNames.{[],join}', 'translucentOverlay') 56 | get overlayClassNamesString() { 57 | let classNames = 58 | (this.overlayClassNames?.join && this.overlayClassNames?.join(' ')) || 59 | this.overlayClassNames; 60 | return [ 61 | 'ember-modal-overlay', 62 | classNames, 63 | this.translucentOverlay ? 'translucent' : null, 64 | this.overlayClass, 65 | ] 66 | .filter((className) => !isEmpty(className)) 67 | .join(' '); 68 | } 69 | 70 | @computed( 71 | 'targetAttachmentClass', 72 | 'variantWrapperClass', 73 | 'wrapperClass', 74 | 'wrapperClassNames.{[],join}', 75 | ) 76 | get wrapperClassNamesString() { 77 | let classNames = 78 | (this.wrapperClassNames?.join && this.wrapperClassNames?.join(' ')) || 79 | this.wrapperClassNames; 80 | return [ 81 | 'ember-modal-wrapper', 82 | classNames, 83 | this.targetAttachmentClass.replace('emd-', 'emd-wrapper-'), 84 | this.variantWrapperClass, 85 | this.wrapperClass, 86 | ] 87 | .filter((className) => !isEmpty(className)) 88 | .join(' '); 89 | } 90 | 91 | @computed('overlayPosition') 92 | get isOverlaySibling() { 93 | return this.overlayPosition === 'sibling'; 94 | } 95 | 96 | @computed('targetAttachment') 97 | get targetAttachmentClass() { 98 | let targetAttachment = this.targetAttachment || ''; 99 | // Convert tether-styled values like 'middle right' to 'right' 100 | targetAttachment = targetAttachment.split(' ').slice(-1)[0]; 101 | return `ember-modal-dialog-target-attachment-${dasherize( 102 | targetAttachment, 103 | )} emd-target-attachment-${dasherize(targetAttachment)}`; 104 | } 105 | 106 | didInsertElement() { 107 | if (!this.clickOutsideToClose) { 108 | return; 109 | } 110 | 111 | this.handleClick = ({ target }) => { 112 | // if the click has already resulted in the target 113 | // being removed or hidden, do nothing 114 | if (target.offsetWidth === 0 && target.offsetHeight === 0) { 115 | return; 116 | } 117 | 118 | if (this.isDestroying || this.isDestroyed) { 119 | return; 120 | } 121 | 122 | let modalSelector = '.ember-modal-dialog'; 123 | if (this.stack) { 124 | modalSelector = '#' + this.stack + modalSelector; 125 | } 126 | 127 | // if the click is within the dialog, do nothing 128 | let modalEl = document.querySelector(modalSelector); 129 | if (modalEl && modalEl.contains(target)) { 130 | return; 131 | } 132 | if (this.onClose) { 133 | this.onClose(); 134 | } 135 | }; 136 | 137 | const registerDelay = clickHandlerDelay(this); 138 | 139 | const registerClick = () => 140 | document.addEventListener('click', this.handleClick); 141 | 142 | // setTimeout needed or else the click handler will catch the click that spawned this modal dialog 143 | setTimeout(registerClick, registerDelay); 144 | 145 | if (isIOS) { 146 | const registerTouch = () => 147 | document.addEventListener('touchend', this.handleClick); 148 | setTimeout(registerTouch, registerDelay); 149 | } 150 | super.didInsertElement(...arguments); 151 | } 152 | 153 | willDestroyElement() { 154 | document.removeEventListener('click', this.handleClick); 155 | if (isIOS) { 156 | document.removeEventListener('touchend', this.handleClick); 157 | } 158 | 159 | super.willDestroyElement(...arguments); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /addon/components/in-place-dialog.hbs: -------------------------------------------------------------------------------- 1 |
11 | {{yield}} 12 |
-------------------------------------------------------------------------------- /addon/components/in-place-dialog.js: -------------------------------------------------------------------------------- 1 | import Component from '@ember/component'; 2 | 3 | export default class InPlaceDialog extends Component { 4 | tagName = ''; 5 | 6 | get containerClassNamesString() { 7 | const addonClassNamesString = [ 8 | 'ember-modal-dialog', 9 | 'ember-modal-dialog-in-place', 10 | 'emd-in-place', 11 | ].join(' '); 12 | 13 | const containerClassNamesString = 14 | (this.containerClassNames?.join && this.containerClassNames?.join(' ')) || 15 | this.containerClassNames || 16 | ''; 17 | 18 | return `${addonClassNamesString} ${containerClassNamesString}`; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /addon/components/liquid-dialog.hbs: -------------------------------------------------------------------------------- 1 | {{#if this.isOverlaySibling}} 2 | 12 |
16 | {{#if this.hasOverlay}} 17 | 21 | {{/if}} 22 |
26 | {{yield}} 27 |
28 |
29 |
30 | {{else}} 31 | 41 | {{#if this.hasOverlay}} 42 | 46 |
50 | {{yield}} 51 |
52 |
53 | {{else}} 54 |
58 | {{yield}} 59 |
60 | {{/if}} 61 |
62 | {{/if}} -------------------------------------------------------------------------------- /addon/components/liquid-dialog.js: -------------------------------------------------------------------------------- 1 | import BasicDialog from './basic-dialog'; 2 | 3 | export default class LiquidDialog extends BasicDialog { 4 | hasOverlay = true; 5 | variantWrapperClass = 'emd-animatable'; 6 | 7 | init() { 8 | super.init(...arguments); 9 | 10 | this.containerClassNames?.push('liquid-dialog'); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /addon/components/liquid-tether-dialog.hbs: -------------------------------------------------------------------------------- 1 | {{#if this.hasOverlay}} 2 | 6 | 10 | 11 | {{/if}} 12 | 26 | {{yield}} 27 | -------------------------------------------------------------------------------- /addon/components/liquid-tether-dialog.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable ember/no-computed-properties-in-native-classes */ 2 | import { computed, set } from '@ember/object'; 3 | import { dasherize } from '@ember/string'; 4 | 5 | import BasicDialog from './basic-dialog'; 6 | 7 | export default class LiquidTetherDialog extends BasicDialog { 8 | @computed('targetAttachment') 9 | get targetAttachmentClass() { 10 | let targetAttachment = this.targetAttachment || ''; 11 | // Convert tether-styled values like 'middle right' to 'right' 12 | targetAttachment = targetAttachment.split(' ').slice(-1)[0]; 13 | return `ember-modal-dialog-target-attachment-${dasherize( 14 | targetAttachment, 15 | )} emd-target-attachment-${dasherize(targetAttachment)}`; 16 | } 17 | 18 | targetAttachment = null; 19 | attachment = null; 20 | 21 | didReceiveAttrs() { 22 | super.didReceiveAttrs(...arguments); 23 | if (!this.attachment) { 24 | set(this, 'attachment', 'middle center'); 25 | } 26 | if (!this.targetAttachment) { 27 | set(this, 'targetAttachment', 'middle center'); 28 | } 29 | } 30 | 31 | @computed 32 | get tetherClassPrefix() { 33 | return 'liquid-tether'; 34 | } 35 | 36 | set tetherClassPrefix(val) { 37 | if (val) { 38 | return val; 39 | } 40 | return 'liquid-tether'; 41 | } 42 | 43 | hasOverlay = true; 44 | 45 | tetherTarget = null; // element, css selector, view instance, 'viewport', or 'scroll-handle' 46 | // offset - passed in 47 | // targetOffset - passed in 48 | // targetModifier - passed in 49 | } 50 | -------------------------------------------------------------------------------- /addon/components/modal-dialog.hbs: -------------------------------------------------------------------------------- 1 | 30 | {{yield}} 31 | -------------------------------------------------------------------------------- /addon/components/modal-dialog.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { inject as service } from '@ember/service'; 3 | import { dasherize } from '@ember/string'; 4 | import { typeOf } from '@ember/utils'; 5 | import { assert, warn } from '@ember/debug'; 6 | import { DEBUG } from '@glimmer/env'; 7 | import { importSync } from '@embroider/macros'; 8 | import { ensureSafeComponent } from '@embroider/util'; 9 | import { guidFor } from '@ember/object/internals'; 10 | 11 | const VALID_OVERLAY_POSITIONS = ['parent', 'sibling']; 12 | 13 | // Args: 14 | // `hasOverlay` | Toggles presence of overlay div in DOM 15 | // `translucentOverlay` | Indicates translucence of overlay, toggles presence of `translucent` CSS selector 16 | // `onClose` | The action handler for the dialog's `onClose` action. This action triggers when the user clicks the modal overlay. 17 | // `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`. 18 | // `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. 19 | // `renderInPlace` | A boolean, when true renders the modal without wormholing or tethering, useful for including a modal in a style guide 20 | // `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'`) 21 | // `containerClass` | CSS class name(s) to append to container divs. Set this from template. 22 | // `containerClassNames` | CSS class names to append to container divs. If you subclass this component, you may define this in your subclass.) 23 | // `overlayClass` | CSS class name(s) to append to overlay divs. Set this from template. 24 | // `overlayClassNames` | CSS class names to append to overlay divs. If you subclass this component, you may define this in your subclass.) 25 | // `wrapperClass` | CSS class name(s) to append to wrapper divs. Set this from template. 26 | // `wrapperClassNames` | CSS class names to append to wrapper divs. If you subclass this component, you may define this in your subclass.) 27 | // `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.) 28 | // `tetherTarget` | If you specify a `tetherTarget`, you are opting into "tethering" behavior, and you must have either `ember-tether` or `liquid-tether` installed. 29 | // `destinationElementId`| optional 30 | // `targetAttachment` | Delegates to Hubspot Tether* 31 | // `tetherClassPrefix` | Delegates to Hubspot Tether* 32 | // `offset` | Delegates to Hubspot Tether* 33 | // `targetOffset` | Delegates to Hubspot Tether* 34 | // `constraints` | Delegates to Hubspot Tether* 35 | // `stack` | Delegates to liquid-wormhole/liquid-tether 36 | // `value` | pass a `value` to set a "value" to be passed to liquid-wormhole / liquid-tether 37 | 38 | export default class ModalDialog extends Component { 39 | @service('modal-dialog') modalService; 40 | 41 | get value() { 42 | // pass a `value` to set a "value" to be passed to liquid-wormhole / liquid-tether 43 | return this.args.value || 0; 44 | } 45 | get hasLiquidWormhole() { 46 | return this.modalService.hasLiquidWormhole; 47 | } 48 | 49 | get hasLiquidTether() { 50 | return this.modalService.hasLiquidTether; 51 | } 52 | 53 | get hasOverlay() { 54 | return this.args.hasOverlay ?? true; 55 | } 56 | 57 | get stack() { 58 | // this `stack` string will be set as this element's ID and passed to liquid-wormhole / liquid-tether 59 | return guidFor(this); 60 | } 61 | 62 | get containerClassNamesVal() { 63 | return this.args.containerClassNames || this.containerClassNames || null; 64 | } 65 | 66 | get attachmentClass() { 67 | let { attachment } = this.args; 68 | if (!attachment) { 69 | return undefined; 70 | } 71 | return attachment 72 | .split(' ') 73 | .map((attachmentPart) => { 74 | return `emd-attachment-${dasherize(attachmentPart)}`; 75 | }) 76 | .join(' '); 77 | } 78 | 79 | get targetAttachment() { 80 | return this.args.targetAttachment || 'middle center'; 81 | } 82 | 83 | get whichModalDialogComponent() { 84 | let { hasLiquidTether, hasLiquidWormhole } = this; 85 | let { animatable, tetherTarget, renderInPlace } = this.args; 86 | let module = importSync('ember-modal-dialog/components/basic-dialog'); 87 | 88 | if (renderInPlace) { 89 | module = importSync('ember-modal-dialog/components/in-place-dialog'); 90 | } else if ( 91 | tetherTarget && 92 | hasLiquidTether && 93 | hasLiquidWormhole && 94 | animatable === true 95 | ) { 96 | module = importSync('ember-modal-dialog/components/liquid-tether-dialog'); 97 | } else if (tetherTarget) { 98 | this.ensureEmberTetherPresent(); 99 | module = importSync('ember-modal-dialog/components/tether-dialog'); 100 | } else if (hasLiquidWormhole && animatable === true) { 101 | module = importSync('ember-modal-dialog/components/liquid-dialog'); 102 | } 103 | 104 | return ensureSafeComponent(module.default, this); 105 | } 106 | 107 | get destinationElementId() { 108 | return ( 109 | this.args.destinationElementId || this.modalService.destinationElementId 110 | ); 111 | } 112 | 113 | validateProps() { 114 | let overlayPosition = this.overlayPosition; 115 | if (VALID_OVERLAY_POSITIONS.indexOf(overlayPosition) === -1) { 116 | warn( 117 | `overlayPosition value '${overlayPosition}' is not valid (valid values [${VALID_OVERLAY_POSITIONS.join( 118 | ', ', 119 | )}])`, 120 | false, 121 | { id: 'ember-modal-dialog.validate-overlay-position' }, 122 | ); 123 | } 124 | } 125 | 126 | get overlayPosition() { 127 | let result = this.args.overlayPosition || 'parent'; 128 | if (DEBUG && VALID_OVERLAY_POSITIONS.indexOf(result) === -1) { 129 | warn( 130 | `overlayPosition value '${result}' is not valid (valid values [${VALID_OVERLAY_POSITIONS.join( 131 | ', ', 132 | )}])`, 133 | false, 134 | { id: 'ember-modal-dialog.validate-overlay-position' }, 135 | ); 136 | } 137 | return result; 138 | } 139 | 140 | ensureEmberTetherPresent() { 141 | if (!this.modalService.hasEmberTether) { 142 | throw new Error( 143 | 'Please install ember-tether in order to pass a tetherTarget to modal-dialog', 144 | ); 145 | } 146 | } 147 | 148 | onCloseAction = () => { 149 | const { onClose } = this.args; 150 | // we shouldn't warn if the callback is not provided at all 151 | if (!onClose) { 152 | return; 153 | } 154 | 155 | assert( 156 | 'onClose handler must be a function', 157 | typeOf(onClose) === 'function', 158 | ); 159 | 160 | onClose(); 161 | }; 162 | 163 | onClickOverlayAction = (ev) => { 164 | ev.preventDefault(); 165 | 166 | const { onClickOverlay } = this.args; 167 | // we shouldn't warn if the callback is not provided at all 168 | if (!onClickOverlay) { 169 | this.onCloseAction(); 170 | return; 171 | } 172 | 173 | assert( 174 | 'onClickOverlay handler must be a function', 175 | typeOf(onClickOverlay) === 'function', 176 | ); 177 | 178 | onClickOverlay(); 179 | }; 180 | } 181 | -------------------------------------------------------------------------------- /addon/components/overlay.hbs: -------------------------------------------------------------------------------- 1 |
8 | {{yield}} 9 |
-------------------------------------------------------------------------------- /addon/components/overlay.js: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { htmlSafe } from '@ember/template'; 3 | 4 | import { isIOS } from '../utils/config-utils'; 5 | 6 | export default class OverlayComponent extends Component { 7 | get cssClasses() { 8 | return htmlSafe(`emd-debug ${isIOS ? 'pointer-cursor' : ''}`); 9 | } 10 | handleClick = (event) => { 11 | this.args.onClickOverlay?.(event); 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /addon/components/positioned-container.js: -------------------------------------------------------------------------------- 1 | import { assert } from '@ember/debug'; 2 | import { typeOf } from '@ember/utils'; 3 | import Component from '@ember/component'; 4 | import { capitalize } from '@ember/string'; 5 | import { observer, computed } from '@ember/object'; 6 | import { on } from '@ember/object/evented'; 7 | 8 | const SUPPORTED_TARGET_ATTACHMENTS = [ 9 | 'top', 10 | 'right', 11 | 'bottom', 12 | 'left', 13 | 'center', 14 | 'elementCenter', 15 | 'none', 16 | ]; 17 | 18 | export default Component.extend({ 19 | // target - element selector, element, or Ember View 20 | // targetAttachment - top, right, bottom, left, center, or none 21 | // left, right, top, bottom (relative to target) 22 | // center (relative to container) 23 | targetAttachment: 'center', 24 | 25 | isPositioned: computed( 26 | 'targetAttachment', 27 | 'target', 28 | 'renderInPlace', 29 | function () { 30 | if (this.renderInPlace) { 31 | return false; 32 | } 33 | let target = this.target; 34 | let targetAttachment = this.targetAttachment; 35 | if ( 36 | target === 'body' && 37 | (targetAttachment === 'center' || targetAttachment === 'middle center') 38 | ) { 39 | return false; 40 | } 41 | 42 | if (target && targetAttachment) { 43 | return true; 44 | } 45 | 46 | return false; 47 | }, 48 | ), 49 | 50 | didGetPositioned: observer( 51 | 'isPositioned', 52 | on('didInsertElement', function () { 53 | if (this._state !== 'inDOM') { 54 | return; 55 | } 56 | 57 | if (this.isPositioned) { 58 | this.updateTargetAttachment(); 59 | } else { 60 | this.element.style.left = ''; 61 | this.element.style.top = ''; 62 | } 63 | }), 64 | ), 65 | 66 | getWrappedTargetAttachmentElement() { 67 | const target = this.target; 68 | if (!target) { 69 | return null; 70 | } 71 | 72 | if (typeOf(target) === 'string') { 73 | const targetSelector = target; 74 | const wrappedElement = document.querySelector(targetSelector); 75 | assert( 76 | `No element found for modal-dialog's target selector '${targetSelector}'.`, 77 | wrappedElement, 78 | ); 79 | return wrappedElement; 80 | } 81 | 82 | // passed an ember view or component 83 | if (target.element) { 84 | return target.element; 85 | } 86 | 87 | // passed an element directly 88 | return target; 89 | }, 90 | 91 | updateTargetAttachment() { 92 | let targetAttachment = this.targetAttachment; 93 | // Convert tether-styled values like 'middle right' to 'right' 94 | targetAttachment = targetAttachment.split(' ').slice(-1)[0]; 95 | assert( 96 | `Positioned container supports targetAttachments of ${SUPPORTED_TARGET_ATTACHMENTS.join( 97 | ', ', 98 | )}`, 99 | SUPPORTED_TARGET_ATTACHMENTS.indexOf(targetAttachment) > -1, 100 | ); 101 | const targetAttachmentMethod = `align${capitalize(targetAttachment)}`; 102 | const targetAttachmentElement = this.getWrappedTargetAttachmentElement(); 103 | 104 | this[targetAttachmentMethod](targetAttachmentElement); 105 | }, 106 | 107 | alignCenter() { 108 | const elementWidth = this.element.offsetWidth; 109 | const elementHeight = this.element.offsetHeight; 110 | 111 | this.element.style.left = '50%'; 112 | this.element.style.top = '50%'; 113 | this.element.style.marginLeft = `${elementWidth * -0.5}px`; 114 | this.element.style.marginTop = `${elementHeight * -0.5}px`; 115 | }, 116 | 117 | alignLeft(targetAttachmentElement) { 118 | assert('Left targetAttachment requires a target', targetAttachmentElement); 119 | 120 | const elementWidth = this.element.offsetWidth; 121 | const originOffset = targetAttachmentElement.getBoundingClientRect(); 122 | const originOffsetTop = originOffset.top; 123 | 124 | this.element.style.left = `${originOffset.left - elementWidth}px`; 125 | this.element.style.top = `${originOffsetTop}px`; 126 | }, 127 | 128 | alignRight(targetAttachmentElement) { 129 | assert('Right targetAttachment requires a target', targetAttachmentElement); 130 | 131 | const targetWidth = targetAttachmentElement.offsetWidth; 132 | const originOffset = targetAttachmentElement.getBoundingClientRect(); 133 | const originOffsetTop = originOffset.top; 134 | 135 | this.element.style.left = `${originOffset.left + targetWidth}px`; 136 | this.element.style.top = `${originOffsetTop}px`; 137 | }, 138 | 139 | alignTop(targetAttachmentElement) { 140 | assert('Top targetAttachment requires a target', targetAttachmentElement); 141 | 142 | const elementWidth = this.element.offsetWidth; 143 | const elementHeight = this.element.offsetHeight; 144 | const originOffset = targetAttachmentElement.getBoundingClientRect(); 145 | const originOffsetTop = originOffset.top; 146 | const targetWidth = targetAttachmentElement.offsetWidth; 147 | 148 | this.element.style.left = `${ 149 | originOffset.left + targetWidth / 2 - elementWidth / 2 150 | }px`; 151 | this.element.style.top = `${originOffsetTop - elementHeight}px`; 152 | }, 153 | 154 | alignBottom(targetAttachmentElement) { 155 | assert( 156 | 'Bottom targetAttachment requires a target', 157 | targetAttachmentElement, 158 | ); 159 | 160 | const elementWidth = this.element.offsetWidth; 161 | const originOffset = targetAttachmentElement.getBoundingClientRect(); 162 | const originOffsetTop = originOffset.top; 163 | const targetWidth = targetAttachmentElement.offsetWidth; 164 | const targetHeight = targetAttachmentElement.offsetHeight; 165 | 166 | this.element.style.left = `${ 167 | originOffset.left + targetWidth / 2 - elementWidth / 2 168 | }px`; 169 | this.element.style.top = `${originOffsetTop + targetHeight}px`; 170 | }, 171 | 172 | alignElementCenter(targetAttachmentElement) { 173 | assert( 174 | 'ElementCenter targetAttachment requires a target', 175 | targetAttachmentElement, 176 | ); 177 | 178 | const elementWidth = this.element.offsetWidth; 179 | const originOffset = targetAttachmentElement.getBoundingClientRect(); 180 | const originOffsetTop = originOffset.top; 181 | const targetWidth = targetAttachmentElement.offsetWidth; 182 | const targetHeight = targetAttachmentElement.offsetHeight; 183 | const elementHeight = this.element.offsetHeight; 184 | 185 | this.element.style.left = `${ 186 | originOffset.left + targetWidth / 2 - elementWidth / 2 187 | }px`; 188 | this.element.style.top = `${ 189 | originOffsetTop + targetHeight / 2 - elementHeight / 2 190 | }px`; 191 | }, 192 | 193 | alignNone() {}, 194 | }); 195 | -------------------------------------------------------------------------------- /addon/components/tether-dialog.hbs: -------------------------------------------------------------------------------- 1 | {{#if this.hasOverlay}} 2 | 3 | 7 | 8 | {{/if}} 9 | 21 | {{yield}} 22 | -------------------------------------------------------------------------------- /addon/components/tether-dialog.js: -------------------------------------------------------------------------------- 1 | import { computed, set } from '@ember/object'; 2 | import { dasherize } from '@ember/string'; 3 | 4 | import BasicDialog from './basic-dialog'; 5 | 6 | export default class TetherDialog extends BasicDialog { 7 | init() { 8 | super.init(...arguments); 9 | this._ensureAttachments(); 10 | } 11 | 12 | @computed('targetAttachment') 13 | get targetAttachmentClass() { 14 | let targetAttachment = this.targetAttachment || ''; 15 | // Convert tether-styled values like 'middle right' to 'right' 16 | targetAttachment = targetAttachment.split(' ').slice(-1)[0]; 17 | return `ember-modal-dialog-target-attachment-${dasherize( 18 | targetAttachment, 19 | )} emd-target-attachment-${dasherize(targetAttachment)}`; 20 | } 21 | 22 | targetAttachment = null; 23 | attachment = null; 24 | 25 | didReceiveAttrs() { 26 | super.didReceiveAttrs(...arguments); 27 | this._ensureAttachments(); 28 | } 29 | 30 | tetherTarget = null; // element, css selector, view instance, 'viewport', or 'scroll-handle' 31 | 32 | @computed 33 | get tetherClassPrefix() { 34 | return 'ember-tether'; 35 | } 36 | 37 | set tetherClassPrefix(val) { 38 | if (val) { 39 | return val; 40 | } 41 | return 'ember-tether'; 42 | } 43 | 44 | // offset - passed in 45 | // targetOffset - passed in 46 | // targetModifier - passed in 47 | _ensureAttachments() { 48 | if (!this.attachment) { 49 | set(this, 'attachment', 'middle center'); 50 | } 51 | if (!this.targetAttachment) { 52 | set(this, 'targetAttachment', 'middle center'); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /addon/helpers/ignore-children.js: -------------------------------------------------------------------------------- 1 | /** 2 | Adopted from https://github.com/ef4/ember-ignore-children-helper 3 | 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2016 7 | 8 | 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: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | 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. 13 | */ 14 | 15 | import { helper } from '@ember/component/helper'; 16 | 17 | export default helper(function ignoreChildren([nextHandler]) { 18 | return function (...args) { 19 | let event = args[args.length - 1]; 20 | if (event && event.target === event.currentTarget) { 21 | nextHandler.apply(this, args); 22 | } 23 | }; 24 | }); 25 | -------------------------------------------------------------------------------- /addon/instance-initializers/add-modals-container.js: -------------------------------------------------------------------------------- 1 | import { getDestinationElementIdFromConfig } from 'ember-modal-dialog/utils/config-utils'; 2 | import { getOwner } from '@ember/application'; 3 | 4 | let hasDOM = typeof document !== 'undefined'; 5 | 6 | function appendContainerElement(rootElementOrId, id) { 7 | if (!hasDOM) { 8 | return; 9 | } 10 | 11 | let rootEl = rootElementOrId.appendChild 12 | ? rootElementOrId 13 | : document.querySelector(rootElementOrId); 14 | 15 | if (rootEl.querySelector('#' + id)) { 16 | return; 17 | } 18 | 19 | let modalContainerEl = document.createElement('div'); 20 | modalContainerEl.id = id; 21 | rootEl.appendChild(modalContainerEl); 22 | } 23 | 24 | export default function (instance) { 25 | let config = instance.resolveRegistration('config:environment'); 26 | let modalContainerElId = getDestinationElementIdFromConfig(config); 27 | 28 | // As there is only a single `Router` across the whole app, which is owned 29 | // by the root `Application`, this reliably finds the root `Application` 30 | // from an `Application` or `Engine`. 31 | // eslint-disable-next-line ember/no-private-routing-service 32 | let app = getOwner(instance.lookup('router:main')); 33 | 34 | appendContainerElement(app.rootElement, modalContainerElId); 35 | } 36 | -------------------------------------------------------------------------------- /addon/utils/config-utils.js: -------------------------------------------------------------------------------- 1 | import { getOwner } from '@ember/application'; 2 | 3 | export function getDestinationElementIdFromConfig(config) { 4 | // if (config.environment === 'test') { 5 | // return 'ember-testing'; 6 | // } 7 | let modalContainerId = 8 | config['ember-modal-dialog'] && 9 | config['ember-modal-dialog'].modalRootElementId; 10 | modalContainerId = modalContainerId || 'modal-overlays'; 11 | return modalContainerId; 12 | } 13 | 14 | export const isIOS = 15 | (globalThis.navigator || false) && 16 | /iPad|iPhone|iPod/.test(navigator.userAgent); 17 | 18 | export function clickHandlerDelay(component) { 19 | let ENV = getOwner(component).resolveRegistration('config:environment'); 20 | if (ENV.environment === 'test') { 21 | return 0; 22 | } 23 | return 300; 24 | } 25 | -------------------------------------------------------------------------------- /app/components/ember-modal-dialog-positioned-container.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-modal-dialog/components/positioned-container'; 2 | -------------------------------------------------------------------------------- /app/components/ember-modal-dialog/-basic-dialog.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-modal-dialog/components/basic-dialog'; 2 | -------------------------------------------------------------------------------- /app/components/ember-modal-dialog/-in-place-dialog.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-modal-dialog/components/in-place-dialog'; 2 | -------------------------------------------------------------------------------- /app/components/ember-modal-dialog/-liquid-dialog.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-modal-dialog/components/liquid-dialog'; 2 | -------------------------------------------------------------------------------- /app/components/ember-modal-dialog/-liquid-tether-dialog.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-modal-dialog/components/liquid-tether-dialog'; 2 | -------------------------------------------------------------------------------- /app/components/ember-modal-dialog/-tether-dialog.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-modal-dialog/components/tether-dialog'; 2 | -------------------------------------------------------------------------------- /app/components/ember-modal-dialog/overlay.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-modal-dialog/components/overlay'; 2 | -------------------------------------------------------------------------------- /app/components/modal-dialog.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-modal-dialog/components/modal-dialog'; 2 | -------------------------------------------------------------------------------- /app/helpers/ignore-children.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-modal-dialog/helpers/ignore-children'; 2 | -------------------------------------------------------------------------------- /app/instance-initializers/add-modals-container.js: -------------------------------------------------------------------------------- 1 | import initialize from 'ember-modal-dialog/instance-initializers/add-modals-container'; 2 | 3 | export default { 4 | name: 'add-modals-container', 5 | initialize, 6 | }; 7 | -------------------------------------------------------------------------------- /app/services/modal-dialog.js: -------------------------------------------------------------------------------- 1 | import { computed } from '@ember/object'; 2 | import Service from '@ember/service'; 3 | import ENV from '../config/environment'; 4 | import { getDestinationElementIdFromConfig } from 'ember-modal-dialog/utils/config-utils'; 5 | 6 | function computedFromConfig(prop) { 7 | return computed(function () { 8 | return ENV['ember-modal-dialog'] && ENV['ember-modal-dialog'][prop]; 9 | }); 10 | } 11 | 12 | export default Service.extend({ 13 | hasEmberTether: computedFromConfig('hasEmberTether'), 14 | hasLiquidWormhole: computedFromConfig('hasLiquidWormhole'), 15 | hasLiquidTether: computedFromConfig('hasLiquidTether'), 16 | destinationElementId: computed(function () { 17 | return getDestinationElementIdFromConfig(ENV); 18 | }), 19 | }); 20 | -------------------------------------------------------------------------------- /app/styles/ember-modal-dialog/ember-modal-appearance.css: -------------------------------------------------------------------------------- 1 | .ember-modal-dialog { 2 | border-radius: 8px; 3 | background-color: #fff; 4 | box-shadow: 0 0 10px #222; 5 | padding: 10px; 6 | } 7 | .ember-modal-overlay.translucent { 8 | background-color: rgba(128, 128, 128, .77); 9 | } 10 | -------------------------------------------------------------------------------- /app/styles/ember-modal-dialog/ember-modal-structure.css: -------------------------------------------------------------------------------- 1 | .ember-modal-dialog { 2 | z-index: 51; 3 | position: fixed; 4 | } 5 | 6 | .ember-modal-dialog.emd-in-place { 7 | position: static; 8 | } 9 | 10 | .ember-modal-wrapper.emd-static.emd-wrapper-target-attachment-center .ember-modal-dialog { 11 | top: 50%; 12 | left: 50%; 13 | transform: translate(-50%, -50%); 14 | } 15 | 16 | .ember-modal-wrapper.emd-animatable.emd-wrapper-target-attachment-center { 17 | width: 100vw; 18 | height: 100vh; 19 | position: fixed; 20 | top: 0; 21 | left: 0; 22 | z-index: 50; 23 | display: flex; 24 | align-items: center; 25 | justify-content: center; 26 | } 27 | 28 | .ember-modal-wrapper.emd-animatable.emd-wrapper-target-attachment-center .ember-modal-overlay { 29 | display: flex; 30 | align-items: center; 31 | justify-content: center; 32 | } 33 | 34 | .ember-modal-wrapper.emd-animatable .ember-modal-dialog { 35 | position: relative; 36 | } 37 | 38 | .ember-modal-overlay { 39 | width: 100vw; 40 | height: 100vh; 41 | position: fixed; 42 | top: 0; 43 | left: 0; 44 | z-index: 50; 45 | } 46 | -------------------------------------------------------------------------------- /codemods.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-modal-dialog/d919ebfaac348f41949b7c74320b28090f05c9fe/codemods.log -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var VersionChecker = require('ember-cli-version-checker'); 4 | 5 | module.exports = function (environment, appConfig, addon) { 6 | appConfig['ember-modal-dialog'] = appConfig['ember-modal-dialog'] || {}; 7 | 8 | var checker = new VersionChecker(addon); 9 | var hasLiquidWormhole = checker.for('liquid-wormhole', 'npm').version; 10 | var hasLiquidTether = checker.for('liquid-tether', 'npm').version; 11 | var hasEmberTether = checker.for('ember-tether', 'npm').version; 12 | 13 | appConfig['ember-modal-dialog']['hasLiquidWormhole'] = hasLiquidWormhole; 14 | appConfig['ember-modal-dialog']['hasLiquidTether'] = hasLiquidTether; 15 | appConfig['ember-modal-dialog']['hasEmberTether'] = hasEmberTether; 16 | 17 | return appConfig; 18 | }; 19 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function (defaults) { 6 | let app = new EmberAddon(defaults, {}); 7 | 8 | /* 9 | This build file specifies the options for the dummy test app of this 10 | addon, located in `/tests/dummy` 11 | This build file does *not* influence how the addon or the app using it 12 | behave. You most likely want to be modifying `./index.js` or app's build file 13 | */ 14 | 15 | const { maybeEmbroider } = require('@embroider/test-setup'); 16 | return maybeEmbroider(app, { 17 | skipBabel: [ 18 | { 19 | package: 'qunit', 20 | }, 21 | ], 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | module.exports = { 7 | name: require('./package').name, 8 | config(env, baseConfig) { 9 | var configPath = path.join(this.root, 'config', 'environment.js'); 10 | 11 | if (fs.existsSync(configPath)) { 12 | var configGenerator = require(configPath); 13 | 14 | return configGenerator(env, baseConfig, this); 15 | } 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-modal-dialog", 3 | "version": "4.1.5", 4 | "description": "An ember-cli addon for implementing modal dialogs", 5 | "keywords": [ 6 | "ember-addon" 7 | ], 8 | "repository": "https://github.com/yapplabs/ember-modal-dialog", 9 | "license": "MIT", 10 | "author": "Yapp Labs and friends", 11 | "directories": { 12 | "doc": "doc", 13 | "test": "tests" 14 | }, 15 | "scripts": { 16 | "preinstall": "npx only-allow pnpm", 17 | "build": "ember build --environment=production", 18 | "deploy": "ember github-pages:commit --message \"Deploy gh-pages from commit $(git rev-parse HEAD)\"; git push; git checkout -", 19 | "lint": "concurrently \"pnpm:lint:*(!fix)\" --names \"lint:\"", 20 | "lint:fix": "concurrently \"pnpm:lint:*:fix\" --names \"fix:\"", 21 | "lint:hbs": "ember-template-lint .", 22 | "lint:hbs:fix": "ember-template-lint . --fix", 23 | "lint:js": "eslint . --cache", 24 | "lint:js:fix": "eslint . --fix", 25 | "release": "release-it", 26 | "start": "ember serve", 27 | "test": "ember test", 28 | "test:ember-compatibility": "ember try:one" 29 | }, 30 | "dependencies": { 31 | "@babel/core": "^7.26.0", 32 | "@embroider/macros": "^1.16.9", 33 | "@embroider/util": "^1.13.2", 34 | "ember-cli-babel": "^8.2.0", 35 | "ember-cli-htmlbars": "^6.3.0", 36 | "ember-cli-version-checker": "^5.1.2", 37 | "ember-wormhole": "^0.6.0" 38 | }, 39 | "devDependencies": { 40 | "@babel/eslint-parser": "^7.25.9", 41 | "@babel/plugin-proposal-decorators": "^7.25.9", 42 | "@ember/optional-features": "^2.1.0", 43 | "@ember/string": "^3.0.0", 44 | "@ember/test-helpers": "^4.0.4", 45 | "@embroider/test-setup": "^4.0.0", 46 | "@glimmer/component": "^1.1.2", 47 | "@glimmer/tracking": "^1.1.2", 48 | "broccoli-asset-rev": "^3.0.0", 49 | "concurrently": "^9.1.0", 50 | "ember-auto-import": "^2.10.0", 51 | "ember-cli": "~5.12.0", 52 | "ember-cli-dependency-checker": "^3.3.2", 53 | "ember-cli-deprecation-workflow": "^3.0.2", 54 | "ember-cli-github-pages": "^0.2.2", 55 | "ember-cli-inject-live-reload": "^2.1.0", 56 | "ember-cli-terser": "^4.0.2", 57 | "ember-disable-prototype-extensions": "^1.1.3", 58 | "ember-load-initializers": "^2.1.2", 59 | "ember-qunit": "^8.1.1", 60 | "ember-resolver": "^13.1.0", 61 | "ember-shiki": "^0.3.0", 62 | "ember-source": "~5.12.0", 63 | "ember-source-channel-url": "^3.0.0", 64 | "ember-template-lint": "^6.0.0", 65 | "ember-template-lint-plugin-prettier": "^5.0.0", 66 | "ember-tether": "^3.1.0", 67 | "ember-try": "^3.0.0", 68 | "eslint": "^8.57.1", 69 | "eslint-config-prettier": "^9.1.0", 70 | "eslint-plugin-ember": "^12.3.3", 71 | "eslint-plugin-n": "^17.14.0", 72 | "eslint-plugin-prettier": "^5.2.1", 73 | "eslint-plugin-qunit": "^8.1.2", 74 | "liquid-fire": "0.34.0", 75 | "liquid-tether": "pzuraq/liquid-tether#7c140024787c3c1b266e89a2ef528eaea898c78e", 76 | "liquid-wormhole": "pzuraq/liquid-wormhole#efa7e5ca518de7c1d6c52666b70bbc0671fcf380", 77 | "loader.js": "^4.7.0", 78 | "only-allow": "^1.2.1", 79 | "prettier": "^3.3.3", 80 | "qunit": "^2.22.0", 81 | "qunit-dom": "^3.3.0", 82 | "release-it": "^13.7.2", 83 | "release-it-lerna-changelog": "^2.4.0", 84 | "webpack": "^5.96.1" 85 | }, 86 | "peerDependencies": { 87 | "@ember/string": "^3.0.0 || ^4.0.0", 88 | "ember-tether": "^3.0.0", 89 | "liquid-tether": "^2.0.7", 90 | "liquid-wormhole": "^3.0.1" 91 | }, 92 | "peerDependenciesMeta": { 93 | "ember-tether": { 94 | "optional": true 95 | }, 96 | "liquid-tether": { 97 | "optional": true 98 | }, 99 | "liquid-wormhole": { 100 | "optional": true 101 | } 102 | }, 103 | "engines": { 104 | "node": "18.* || >= 20" 105 | }, 106 | "publishConfig": { 107 | "registry": "https://registry.npmjs.org" 108 | }, 109 | "ember": { 110 | "edition": "octane" 111 | }, 112 | "ember-addon": { 113 | "configPath": "tests/dummy/config", 114 | "demoURL": "http://yapplabs.github.io/ember-modal-dialog/", 115 | "versionCompatibility": { 116 | "ember": "~3.20.0 || ~3.24.0 || >= 3.25.0" 117 | } 118 | }, 119 | "release-it": { 120 | "plugins": { 121 | "release-it-lerna-changelog": { 122 | "infile": "CHANGELOG.md", 123 | "launchEditor": false 124 | } 125 | }, 126 | "git": { 127 | "tagName": "v${version}" 128 | }, 129 | "github": { 130 | "release": true, 131 | "tokenRef": "GITHUB_AUTH" 132 | } 133 | }, 134 | "volta": { 135 | "node": "20.14.0", 136 | "pnpm": "9.14.2" 137 | }, 138 | "pnpm": { 139 | "overrides": { 140 | "perf-primitives": "0.0.6" 141 | }, 142 | "patchedDependencies": { 143 | "ember-shiki@0.3.0": "patches/ember-shiki@0.3.0.patch" 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /patches/ember-shiki@0.3.0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/dist/components/code-block.js b/dist/components/code-block.js 2 | index 1b4eb37ff47f97bf032c3f63de003531fc813409..74c783ee5121a77bcdc9a9b227d1113cf11359f4 100644 3 | --- a/dist/components/code-block.js 4 | +++ b/dist/components/code-block.js 5 | @@ -1,6 +1,6 @@ 6 | import { _ as _applyDecoratedDescriptor, a as _initializerDefineProperty, b as _defineProperty } from '../_rollupPluginBabelHelpers-e795903d.js'; 7 | import { getOwner } from '@ember/application'; 8 | -import { service } from '@ember/service'; 9 | +import { inject as service } from '@ember/service'; 10 | import Component from '@glimmer/component'; 11 | import { tracked } from '@glimmer/tracking'; 12 | import { htmlSafe } from '@ember/template'; 13 | diff --git a/dist/modifiers/shiki-render.js b/dist/modifiers/shiki-render.js 14 | index 06f9173b4d507e9121e208da20a6be606e5c59a2..2f8f4c60c5a0c1450b66313e91f3f90c35271e80 100644 15 | --- a/dist/modifiers/shiki-render.js 16 | +++ b/dist/modifiers/shiki-render.js 17 | @@ -1,7 +1,7 @@ 18 | import { _ as _applyDecoratedDescriptor, a as _initializerDefineProperty, b as _defineProperty } from '../_rollupPluginBabelHelpers-e795903d.js'; 19 | import { buildTask } from 'ember-concurrency/-private/async-arrow-runtime'; 20 | import Modifier from 'ember-modifier'; 21 | -import { service } from '@ember/service'; 22 | +import { inject as service } from '@ember/service'; 23 | import 'ember-concurrency'; 24 | 25 | var _class, _descriptor; 26 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_start_timeout: 120, 9 | browser_args: { 10 | Chrome: { 11 | ci: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.CI ? '--no-sandbox' : null, 14 | '--headless', 15 | '--disable-dev-shm-usage', 16 | '--disable-software-rasterizer', 17 | '--mute-audio', 18 | '--remote-debugging-port=0', 19 | '--window-size=1440,900', 20 | ].filter(Boolean), 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /tests/acceptance/animatable-test.js: -------------------------------------------------------------------------------- 1 | import { settled, visit } from '@ember/test-helpers'; 2 | import { module, test } from 'qunit'; 3 | import { setupApplicationTest } from 'ember-qunit'; 4 | 5 | const modalRootElementSelector = '#modal-overlays'; 6 | const overlaySelector = '[data-emd-overlay]'; 7 | const wrapperSelector = '.ember-modal-wrapper'; 8 | const dialogSelector = '.ember-modal-dialog'; 9 | const dialogCloseButton = [dialogSelector, 'button'].join(' '); 10 | 11 | module('Acceptance: modal-dialog | animatable', function (hooks) { 12 | setupApplicationTest(hooks); 13 | hooks.beforeEach(async function () { 14 | await visit('/animatable'); 15 | }); 16 | 17 | hooks.afterEach(async function () { 18 | await settled(); 19 | }); 20 | 21 | test('basic modal', async function (assert) { 22 | assert.dom(modalRootElementSelector).exists({ count: 1 }); 23 | assert.isAbsent(overlaySelector); 24 | assert.dom('#example-basic button').exists({ count: 2 }); 25 | 26 | await assert.dialogOpensAndCloses({ 27 | openSelector: '#example-basic button', 28 | dialogText: 'Basic', 29 | closeSelector: wrapperSelector, 30 | }); 31 | 32 | await assert.dialogOpensAndCloses({ 33 | openSelector: '#example-basic button', 34 | dialogText: 'Basic', 35 | closeSelector: dialogCloseButton, 36 | }); 37 | }); 38 | 39 | test('modal with translucent overlay', async function (assert) { 40 | await assert.dialogOpensAndCloses({ 41 | openSelector: '#example-translucent button', 42 | dialogText: 'With Translucent Overlay', 43 | closeSelector: wrapperSelector, 44 | }); 45 | 46 | await assert.dialogOpensAndCloses({ 47 | openSelector: '#example-translucent button', 48 | dialogText: 'With Translucent Overlay', 49 | closeSelector: dialogCloseButton, 50 | }); 51 | }); 52 | 53 | test('modal with custom styles', async function (assert) { 54 | await assert.dialogOpensAndCloses({ 55 | openSelector: '#example-custom-styles button', 56 | dialogText: 'Custom Styles', 57 | closeSelector: overlaySelector, 58 | whileOpen() { 59 | assert 60 | .dom(overlaySelector) 61 | .hasClass('custom-styles-overlay', 'has provided overlayClass'); 62 | assert 63 | .dom(dialogSelector) 64 | .hasClass( 65 | 'custom-styles-modal-container', 66 | 'has provided containerClass', 67 | ); 68 | }, 69 | }); 70 | await assert.dialogOpensAndCloses({ 71 | openSelector: '#example-custom-styles button', 72 | dialogText: 'Custom Styles', 73 | closeSelector: dialogCloseButton, 74 | }); 75 | }); 76 | 77 | test('subclassed modal', async function (assert) { 78 | await assert.dialogOpensAndCloses({ 79 | openSelector: '#example-subclass button', 80 | dialogText: 'Via Subclass', 81 | closeSelector: overlaySelector, 82 | whileOpen() { 83 | assert 84 | .dom(dialogSelector) 85 | .hasClass('my-cool-modal', 'has provided containerClassNames'); 86 | }, 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /tests/acceptance/basic-test.js: -------------------------------------------------------------------------------- 1 | import { click, find, settled, visit } from '@ember/test-helpers'; 2 | import { module, test } from 'qunit'; 3 | import { findContains } from '../helpers/modal-asserts'; 4 | import { setupApplicationTest } from 'ember-qunit'; 5 | 6 | const modalRootElementSelector = '#modal-overlays'; 7 | const overlaySelector = '.ember-modal-overlay'; 8 | const dialogSelector = '.ember-modal-dialog'; 9 | const dialogCloseButton = [dialogSelector, 'button'].join(' '); 10 | 11 | module('Acceptance: modal-dialog | no animation, no tether', function (hooks) { 12 | setupApplicationTest(hooks); 13 | hooks.beforeEach(async function () { 14 | await visit('/'); 15 | }); 16 | 17 | hooks.afterEach(async function () { 18 | await settled(); 19 | }); 20 | 21 | test('basic modal', async function (assert) { 22 | assert.dom(modalRootElementSelector).exists({ count: 1 }); 23 | assert.isAbsent(overlaySelector); 24 | assert.dom('#example-basic button').exists({ count: 2 }); 25 | 26 | await assert.dialogOpensAndCloses({ 27 | openSelector: '#example-basic button', 28 | dialogText: 'Basic', 29 | closeSelector: overlaySelector, 30 | }); 31 | 32 | await assert.dialogOpensAndCloses({ 33 | openSelector: '#example-basic button', 34 | dialogText: 'Basic', 35 | closeSelector: dialogCloseButton, 36 | }); 37 | }); 38 | 39 | test('modal with translucent overlay', async function (assert) { 40 | await assert.dialogOpensAndCloses({ 41 | openSelector: '#example-translucent button', 42 | dialogText: 'With Translucent Overlay', 43 | closeSelector: overlaySelector, 44 | }); 45 | 46 | await assert.dialogOpensAndCloses({ 47 | openSelector: '#example-translucent button', 48 | dialogText: 'With Translucent Overlay', 49 | closeSelector: dialogCloseButton, 50 | }); 51 | }); 52 | 53 | test('modal without overlay', async function (assert) { 54 | await assert.dialogOpensAndCloses({ 55 | openSelector: '#example-without-overlay button', 56 | dialogText: 'Without Overlay', 57 | closeSelector: '#example-without-overlay', 58 | }); 59 | 60 | await assert.dialogOpensAndCloses({ 61 | openSelector: '#example-without-overlay button', 62 | dialogText: 'Without Overlay', 63 | closeSelector: dialogCloseButton, 64 | }); 65 | }); 66 | 67 | test('modal with overlay', async function (assert) { 68 | await assert.dialogOpensAndCloses({ 69 | openSelector: '#example-translucent button', 70 | dialogText: 'With Translucent Overlay', 71 | closeSelector: overlaySelector, 72 | }); 73 | 74 | await assert.dialogOpensAndCloses({ 75 | openSelector: '#example-translucent button', 76 | dialogText: 'With Translucent Overlay', 77 | closeSelector: dialogCloseButton, 78 | }); 79 | }); 80 | 81 | test('modal with sibling overlay', async function (assert) { 82 | await assert.dialogOpensAndCloses({ 83 | openSelector: '#example-overlay-sibling button', 84 | dialogText: 'With Translucent Overlay as Sibling', 85 | closeSelector: overlaySelector, 86 | }); 87 | 88 | await assert.dialogOpensAndCloses({ 89 | openSelector: '#example-overlay-sibling button', 90 | dialogText: 'With Translucent Overlay as Sibling', 91 | closeSelector: dialogCloseButton, 92 | }); 93 | }); 94 | 95 | test('clicking translucent overlay triggers callback', async function (assert) { 96 | window.onClickOverlayCallbackCalled = false; 97 | 98 | await click('#example-translucent-with-callback button'); 99 | await click(overlaySelector); 100 | 101 | assert.dom(overlaySelector).exists({ count: 1 }); 102 | assert.ok(window.onClickOverlayCallbackCalled); 103 | 104 | await click(dialogCloseButton); 105 | 106 | assert.isAbsent(overlaySelector); 107 | }); 108 | 109 | test('modal with custom styles', async function (assert) { 110 | await assert.dialogOpensAndCloses({ 111 | openSelector: '#example-custom-styles button', 112 | dialogText: 'Custom Styles', 113 | closeSelector: overlaySelector, 114 | whileOpen() { 115 | assert 116 | .dom(`#ember-testing ${overlaySelector}`) 117 | .hasClass('custom-styles-overlay', 'has provided overlayClass'); 118 | assert 119 | .dom(`#ember-testing ${dialogSelector}`) 120 | .hasClass( 121 | 'custom-styles-modal-container', 122 | 'has provided container-class', 123 | ); 124 | }, 125 | }); 126 | await assert.dialogOpensAndCloses({ 127 | openSelector: '#example-custom-styles button', 128 | dialogText: 'Custom Styles', 129 | closeSelector: dialogCloseButton, 130 | }); 131 | }); 132 | 133 | test('target - selector', async function (assert) { 134 | await assert.dialogOpensAndCloses({ 135 | openSelector: '#example-target-selector button', 136 | dialogText: 'Target - Selector', 137 | closeSelector: dialogCloseButton, 138 | whileOpen() { 139 | assert 140 | .dom(dialogSelector) 141 | .hasClass( 142 | 'ember-modal-dialog-target-attachment-left', 143 | 'has targetAttachment class name', 144 | ); 145 | }, 146 | }); 147 | }); 148 | 149 | test('target - element', async function (assert) { 150 | await assert.dialogOpensAndCloses({ 151 | openSelector: '#example-target-element button', 152 | dialogText: 'Target - Element', 153 | closeSelector: dialogCloseButton, 154 | }); 155 | }); 156 | 157 | test('subclassed modal', async function (assert) { 158 | await assert.dialogOpensAndCloses({ 159 | openSelector: '#example-subclass button', 160 | dialogText: 'Via Subclass', 161 | closeSelector: overlaySelector, 162 | whileOpen() { 163 | assert 164 | .dom(dialogSelector) 165 | .hasClass('my-cool-modal', 'has provided containerClassNames'); 166 | }, 167 | }); 168 | }); 169 | 170 | test('subclassed modal with string for containerClassNames', async function (assert) { 171 | await assert.dialogOpensAndCloses({ 172 | openSelector: '#example-subclass-2 button', 173 | dialogText: 'Via Subclass', 174 | closeSelector: overlaySelector, 175 | whileOpen() { 176 | assert 177 | .dom(dialogSelector) 178 | .hasClass('my-cool-modal', 'has provided containerClassNames'); 179 | assert 180 | .dom(dialogSelector) 181 | .hasClass('my-cool-modal-2', 'has provided containerClassNames'); 182 | }, 183 | }); 184 | }); 185 | 186 | test('in place', async function (assert) { 187 | await click('#example-in-place button'); 188 | let dialogText = 'In Place'; 189 | let inPlaceDialogSelector = `${dialogSelector}.ember-modal-dialog-in-place`; 190 | let inPlaceRootSelector = '#container-in-place'; 191 | let inPlaceCloseButton = [ 192 | inPlaceRootSelector, 193 | inPlaceDialogSelector, 194 | 'button', 195 | ].join(' '); 196 | let dialogElement = find(inPlaceDialogSelector); 197 | 198 | assert 199 | .dom(inPlaceDialogSelector) 200 | .hasClass('my-custom-class', 'has provided containerClass'); 201 | assert.strictEqual( 202 | getComputedStyle(dialogElement).getPropertyValue('position'), 203 | 'static', 204 | 'not absolutely positioned', 205 | ); 206 | assert.strictEqual( 207 | getComputedStyle(dialogElement).getPropertyValue('left'), 208 | 'auto', 209 | 'should not be positioned (left)', 210 | ); 211 | assert.strictEqual( 212 | getComputedStyle(dialogElement).getPropertyValue('margin-left'), 213 | '0px', 214 | 'should not be positioned (margin-left)', 215 | ); 216 | assert.strictEqual( 217 | findContains(`${modalRootElementSelector} ${dialogSelector}`, dialogText), 218 | undefined, 219 | 'dialog is not open', 220 | ); 221 | assert.ok( 222 | findContains(`${inPlaceRootSelector} ${dialogSelector}`, dialogText), 223 | 'dialog rendered in place, once', 224 | ); 225 | 226 | await click(inPlaceCloseButton); 227 | assert.strictEqual( 228 | findContains(`${modalRootElementSelector} ${dialogSelector}`, dialogText), 229 | undefined, 230 | 'dialog is not open', 231 | ); 232 | assert.strictEqual( 233 | findContains(`${inPlaceRootSelector} ${dialogSelector}`, dialogText), 234 | undefined, 235 | 'dialog is not rendered in place', 236 | ); 237 | 238 | await click('#example-in-place-2 button'); 239 | inPlaceDialogSelector = `${dialogSelector}.ember-modal-dialog-in-place`; 240 | inPlaceRootSelector = '#container-in-place-2'; 241 | inPlaceCloseButton = [ 242 | inPlaceRootSelector, 243 | inPlaceDialogSelector, 244 | 'button', 245 | ].join(' '); 246 | 247 | assert 248 | .dom(inPlaceDialogSelector) 249 | .hasClass('my-custom-class-2', 'has provided containerClassNames'); 250 | await click(inPlaceCloseButton); 251 | }); 252 | }); 253 | -------------------------------------------------------------------------------- /tests/acceptance/tethered-animatable-test.js: -------------------------------------------------------------------------------- 1 | import { settled, visit } from '@ember/test-helpers'; 2 | import { module, test } from 'qunit'; 3 | import { setupApplicationTest } from 'ember-qunit'; 4 | 5 | const dialogSelector = '.ember-modal-dialog'; 6 | const dialogCloseButton = [dialogSelector, 'button'].join(' '); 7 | 8 | module('Acceptance: modal-dialog | tethered and animatable', function (hooks) { 9 | setupApplicationTest(hooks); 10 | hooks.beforeEach(async function () { 11 | await visit('/tethered-animatable'); 12 | }); 13 | 14 | hooks.afterEach(async function () { 15 | await settled(); 16 | }); 17 | 18 | test('target - selector', async function (assert) { 19 | await assert.dialogOpensAndCloses({ 20 | openSelector: '#example-target-selector button', 21 | dialogText: 'Target - Selector', 22 | closeSelector: dialogCloseButton, 23 | hasOverlay: false, 24 | whileOpen() { 25 | assert 26 | .dom(dialogSelector) 27 | .hasClass( 28 | 'liquid-tether-target-attached-left', 29 | 'has targetAttachment class name', 30 | ); 31 | }, 32 | }); 33 | }); 34 | 35 | test('target - element', async function (assert) { 36 | await assert.dialogOpensAndCloses({ 37 | openSelector: '#example-target-element button', 38 | dialogText: 'Target - Element', 39 | closeSelector: dialogCloseButton, 40 | hasOverlay: false, 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /tests/acceptance/tethered-test.js: -------------------------------------------------------------------------------- 1 | import { settled, visit } from '@ember/test-helpers'; 2 | import { module, test } from 'qunit'; 3 | import { setupApplicationTest } from 'ember-qunit'; 4 | 5 | const dialogSelector = '.ember-modal-dialog'; 6 | const dialogCloseButton = [dialogSelector, 'button'].join(' '); 7 | 8 | module('Acceptance: modal-dialog | tethered', function (hooks) { 9 | setupApplicationTest(hooks); 10 | 11 | hooks.afterEach(async function () { 12 | await settled(); 13 | }); 14 | 15 | test('target - selector', async function (assert) { 16 | await visit('/tethered'); 17 | assert.dialogOpensAndCloses({ 18 | openSelector: '#example-target-selector button', 19 | dialogText: 'Target - Selector', 20 | closeSelector: dialogCloseButton, 21 | hasOverlay: false, 22 | whileOpen() { 23 | assert 24 | .dom(dialogSelector) 25 | .hasClass( 26 | 'ember-tether-target-attached-left', 27 | 'has targetAttachment class name', 28 | ); 29 | }, 30 | }); 31 | }); 32 | 33 | test('target - element', async function (assert) { 34 | await visit('/tethered'); 35 | assert.dialogOpensAndCloses({ 36 | openSelector: '#example-target-element button', 37 | dialogText: 'Target - Element', 38 | closeSelector: dialogCloseButton, 39 | hasOverlay: false, 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'dummy/config/environment'; 5 | 6 | export default class App extends Application { 7 | modulePrefix = config.modulePrefix; 8 | podModulePrefix = config.podModulePrefix; 9 | Resolver = Resolver; 10 | } 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/components/demo-modal.hbs: -------------------------------------------------------------------------------- 1 |
2 |

{{@title}}

3 | 4 |
5 | {{yield 6 | (hash 7 | OpenButton=(component 8 | "demo-modal/button" 9 | label="Open" 10 | onClick=this.openModal 11 | ) 12 | ) 13 | to="menu" 14 | }} 15 |
16 | 17 |
18 | {{yield to="code"}} 19 |
20 | 21 |
22 | {{yield 23 | (hash 24 | CloseButton=(component 25 | "demo-modal/button" 26 | label="Close" 27 | onClick=this.closeModal 28 | ) 29 | closeModal=this.closeModal 30 | isModalOpen=this.isModalOpen 31 | ) 32 | }} 33 |
34 |
-------------------------------------------------------------------------------- /tests/dummy/app/components/demo-modal.js: -------------------------------------------------------------------------------- 1 | import { action } from '@ember/object'; 2 | import Component from '@glimmer/component'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | export default class DemoModal extends Component { 6 | @tracked isModalOpen = false; 7 | 8 | @action closeModal() { 9 | this.isModalOpen = false; 10 | 11 | this.args.onClose?.(); 12 | } 13 | 14 | @action openModal() { 15 | this.isModalOpen = true; 16 | 17 | this.args.onOpen?.(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/dummy/app/components/demo-modal/button.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/components/demo-modal/button.js: -------------------------------------------------------------------------------- 1 | import templateOnlyComponent from '@ember/component/template-only'; 2 | 3 | const DemoModalButton = templateOnlyComponent(); 4 | 5 | export default DemoModalButton; 6 | -------------------------------------------------------------------------------- /tests/dummy/app/components/my-cool-modal-dialog-two.js: -------------------------------------------------------------------------------- 1 | import ModalDialogComponent from 'ember-modal-dialog/components/modal-dialog'; 2 | 3 | export default class MyCoolModalDialogTwo extends ModalDialogComponent { 4 | translucentOverlay = true; // override default of false 5 | containerClassNames = 'my-cool-modal my-cool-modal-2'; 6 | destinationElementId = 'modal-overlays'; 7 | } 8 | -------------------------------------------------------------------------------- /tests/dummy/app/components/my-cool-modal-dialog.js: -------------------------------------------------------------------------------- 1 | import ModalDialogComponent from 'ember-modal-dialog/components/modal-dialog'; 2 | 3 | export default class MyCoolModalDialog extends ModalDialogComponent { 4 | translucentOverlay = true; // override default of false 5 | containerClassNames = ['my-cool-modal']; 6 | destinationElementId = 'modal-overlays'; 7 | } 8 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/animatable.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | import { codeSnippets } from '../utils/code-snippets/animatable'; 4 | 5 | export default class AnimatableController extends Controller { 6 | codeSnippets = codeSnippets; 7 | } 8 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/index.js: -------------------------------------------------------------------------------- 1 | import { action } from '@ember/object'; 2 | import Controller from '@ember/controller'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | import { codeSnippets } from '../utils/code-snippets/index'; 6 | 7 | export default class IndexController extends Controller { 8 | codeSnippets = codeSnippets; 9 | 10 | @tracked exampleTargetAttachment = 'middle left'; 11 | @tracked exampleAttachment = 'middle right'; 12 | 13 | nextAttachment(val) { 14 | switch (val) { 15 | case 'middle right': 16 | return 'bottom center'; 17 | case 'bottom center': 18 | return 'middle left'; 19 | case 'middle left': 20 | return 'top center'; 21 | case 'top center': 22 | return 'middle right'; 23 | } 24 | return false; 25 | } 26 | 27 | @action onClickTranslucentOverlay() { 28 | window.onClickOverlayCallbackCalled = true; 29 | } 30 | 31 | @action onCloseCenteredScrolling() { 32 | document.querySelector('#modal-overlays').classList.remove('active'); 33 | document.body.classList.remove('centered-modal-showing'); 34 | } 35 | 36 | @action onOpenCenteredScrolling() { 37 | document.querySelector('#modal-overlays').classList.add('active'); 38 | document.body.classList.add('centered-modal-showing'); 39 | } 40 | 41 | @action toggleTarget() { 42 | const newTargetAttachment = this.nextAttachment( 43 | this.exampleTargetAttachment, 44 | ); 45 | const newAttachment = this.nextAttachment(this.exampleAttachment); 46 | 47 | this.exampleTargetAttachment = newTargetAttachment; 48 | this.exampleAttachment = newAttachment; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/tethered-animatable.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import { action } from '@ember/object'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | import { codeSnippets } from '../utils/code-snippets/tethered-animatable'; 6 | 7 | export default class TetheredAnimatableController extends Controller { 8 | codeSnippets = codeSnippets; 9 | 10 | @tracked exampleTargetAttachment = 'middle left'; 11 | @tracked exampleAttachment = 'middle right'; 12 | @tracked isShowingSeparateStacksModal1 = false; 13 | @tracked isShowingSeparateStacksModal2 = false; 14 | @tracked isShowingSeparateStacksModal3 = false; 15 | 16 | nextAttachment(val) { 17 | switch (val) { 18 | case 'middle right': 19 | return 'bottom center'; 20 | case 'bottom center': 21 | return 'middle left'; 22 | case 'middle left': 23 | return 'top center'; 24 | case 'top center': 25 | return 'middle right'; 26 | } 27 | return false; 28 | } 29 | 30 | @action closeSeparateStackModal1() { 31 | this.isShowingSeparateStacksModal1 = false; 32 | } 33 | 34 | @action closeSeparateStackModal2() { 35 | this.isShowingSeparateStacksModal2 = false; 36 | } 37 | 38 | @action closeSeparateStackModal3() { 39 | this.isShowingSeparateStacksModal3 = false; 40 | } 41 | 42 | @action openSeparateStackModal1() { 43 | this.isShowingSeparateStacksModal1 = true; 44 | } 45 | 46 | @action openSeparateStackModal2() { 47 | this.isShowingSeparateStacksModal2 = true; 48 | } 49 | 50 | @action openSeparateStackModal3() { 51 | this.isShowingSeparateStacksModal3 = true; 52 | } 53 | 54 | @action toggleTarget() { 55 | const newTargetAttachment = this.nextAttachment( 56 | this.exampleTargetAttachment, 57 | ); 58 | const newAttachment = this.nextAttachment(this.exampleAttachment); 59 | 60 | this.exampleTargetAttachment = newTargetAttachment; 61 | this.exampleAttachment = newAttachment; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/tethered.js: -------------------------------------------------------------------------------- 1 | import { action } from '@ember/object'; 2 | import Controller from '@ember/controller'; 3 | import { tracked } from '@glimmer/tracking'; 4 | 5 | import { codeSnippets } from '../utils/code-snippets/tethered'; 6 | 7 | export default class TetheredController extends Controller { 8 | codeSnippets = codeSnippets; 9 | 10 | @tracked exampleTargetAttachment = 'middle left'; 11 | @tracked exampleAttachment = 'middle right'; 12 | 13 | nextAttachment(val) { 14 | switch (val) { 15 | case 'middle right': 16 | return 'bottom center'; 17 | case 'bottom center': 18 | return 'middle left'; 19 | case 'middle left': 20 | return 'top center'; 21 | case 'top center': 22 | return 'middle right'; 23 | } 24 | return false; 25 | } 26 | 27 | @action toggleTarget() { 28 | const newTargetAttachment = this.nextAttachment( 29 | this.exampleTargetAttachment, 30 | ); 31 | const newAttachment = this.nextAttachment(this.exampleAttachment); 32 | 33 | this.exampleTargetAttachment = newTargetAttachment; 34 | this.exampleAttachment = newAttachment; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/lorem-ipsum.js: -------------------------------------------------------------------------------- 1 | import { helper } from '@ember/component/helper'; 2 | import { htmlSafe } from '@ember/template'; 3 | 4 | let originalText = 5 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, ' + 6 | 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' + 7 | 'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut ' + 8 | 'aliquip ex ea commodo consequat. ' + 9 | 'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore ' + 10 | 'eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, ' + 11 | 'sunt in culpa qui officia deserunt mollit anim id est laborum'; 12 | 13 | export function loremIpsum(params, hash) { 14 | var text = originalText; 15 | 16 | if (!hash || !hash.length || hash.length < 0) { 17 | // return the whole paragraph 18 | } else { 19 | if (hash.length < text.length) { 20 | text = text.substring(0, hash.length); 21 | } else { 22 | var result = '', 23 | repeatN = hash.length / text.length; 24 | 25 | for (var i = 0; i < repeatN; i++) { 26 | result += text; 27 | result += i === repeatN - 1 ? '' : '. '; 28 | } 29 | var remainder = hash.length % text.length; 30 | result += text.substring(0, remainder); 31 | text = result; 32 | } 33 | } 34 | 35 | text += '.'; 36 | 37 | return new htmlSafe('

' + text + '

'); 38 | } 39 | 40 | export default helper(loremIpsum); 41 | -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Ember Modal Dialog 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'dummy/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () { 10 | this.route('tethered'); 11 | this.route('animatable'); 12 | this.route('tethered-animatable'); 13 | }); 14 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | @import "./ember-modal-dialog/ember-modal-structure.css"; 2 | @import "./ember-modal-dialog/ember-modal-appearance.css"; 3 | 4 | body { 5 | font-family: sans-serif; 6 | font-size: 12px; 7 | margin: 0; 8 | } 9 | 10 | .ApplicationRoute { 11 | margin: 20px; 12 | } 13 | 14 | .ApplicationRoute-summary { 15 | font-size: 1.1rem; 16 | margin-bottom: 10px; 17 | } 18 | 19 | .ApplicationRoute-nav { 20 | margin-bottom: 20px; 21 | padding: 0; 22 | } 23 | 24 | .ApplicationRoute-nav > li { 25 | display: inline-block; 26 | } 27 | 28 | .ApplicationRoute-nav > li::after { 29 | content: ' | '; 30 | } 31 | 32 | .ApplicationRoute-nav > li:last-child::after { 33 | content: ''; 34 | } 35 | 36 | .example { 37 | padding: 10px; 38 | background: #ddd; 39 | border: solid 1px #666; 40 | border-radius: 4px; 41 | margin: 0 5px 5px 0; 42 | min-height: 120px; 43 | width: 800px; 44 | max-width: 93%; 45 | } 46 | 47 | #container-in-place { 48 | min-height: 10px; 49 | border: dotted 2px lime; 50 | padding: 20px; 51 | } 52 | 53 | button { 54 | padding: 4px 8px; 55 | border-radius: 4px; 56 | background-color: #0f9d58; 57 | color: #fff; 58 | cursor: pointer; 59 | outline: none; 60 | } 61 | 62 | pre { 63 | border: solid 1px #bbb; 64 | border-radius: 4px; 65 | } 66 | 67 | h1, 68 | h2 { 69 | padding: 0 0 8px 0; 70 | margin: 0; 71 | } 72 | 73 | .my-cool-modal { 74 | border-radius: 100px; 75 | padding: 40px; 76 | } 77 | 78 | .my-cool-modal-2 { 79 | border-width: 5px; 80 | border-color: blue; 81 | } 82 | 83 | .custom-styles-overlay { 84 | background-color: rgba(15, 157, 88, .77); 85 | } 86 | 87 | .custom-styles-overlay, 88 | .custom-styles-wrapper { 89 | display: flex; 90 | justify-content: flex-end; 91 | align-items: flex-start; 92 | width: 100vw; 93 | height: 100vh; 94 | position: fixed; 95 | top: 0; 96 | left: 0; 97 | } 98 | 99 | .custom-styles-modal-container { 100 | background: #222; 101 | color: white; 102 | margin-left: -25px; 103 | margin-top: 10px; 104 | width: 300px; 105 | 106 | h1 { 107 | color: #0f9d58; 108 | } 109 | } 110 | 111 | .ember-modal-overlay-in-place { 112 | height: initial; 113 | opacity: initial; 114 | position: initial; 115 | } 116 | 117 | .emd-in-place { 118 | position: initial; 119 | } 120 | 121 | body.centered-modal-showing { 122 | overflow: hidden; 123 | } 124 | .centered-scrolling-wrapper { 125 | width: 100vw; 126 | height: 100vh; 127 | position: fixed; 128 | top: 0; 129 | left: 0; 130 | overflow-y: scroll; 131 | z-index: 1; 132 | } 133 | 134 | .centered-scrolling-overlay { 135 | position: relative; 136 | height: auto; 137 | min-height: 100vh; 138 | padding: 1em; 139 | display: flex; 140 | align-items: flex-start; 141 | justify-content: center; 142 | } 143 | 144 | /* basic modal style (an example, this is not necessary for the centering) */ 145 | .centered-scrolling-container { 146 | position: relative; 147 | background-color: white; 148 | min-width: 30em; 149 | max-width: 650px; 150 | min-height: 20em; 151 | padding: 3em; 152 | margin-top: 30px; 153 | margin-bottom: 30px; 154 | box-sizing: border-box; 155 | box-shadow: 0px 4px 25px 4px rgba(0,0,0,0.30); 156 | } 157 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/animatable.hbs: -------------------------------------------------------------------------------- 1 |

2 | Animatable examples of the 3 | modal-dialog 4 | component 5 |

6 | 7 |

8 | If 9 | liquid-wormhole 11 | is installed and `animatable=true` is passed, the component 12 | will automatically become animatable using 13 | liquid-fire. 14 |

15 | 16 |
17 |

app/transitions.js for these examples

18 | 19 | 23 |
24 | 25 | 26 | <:menu as |M|> 27 | 28 | 29 | 30 | <:code> 31 | 38 | 39 | 40 | <:default as |D|> 41 | {{#if D.isModalOpen}} 42 | 48 |

Stop! Modal Time!

49 |

Basic

50 | 51 |
52 | {{/if}} 53 | 54 |
55 | 56 | 60 | <:menu as |M|> 61 | 62 | 63 | 64 | <:code> 65 | 72 | 73 | 74 | <:default as |D|> 75 | {{#if D.isModalOpen}} 76 | 83 |

Stop! Modal Time!

84 |

With Translucent Overlay

85 | 86 |
87 | {{/if}} 88 | 89 |
90 | 91 | 95 | <:menu as |M|> 96 | 97 | 98 | 99 | <:code> 100 | 107 | 108 | 109 | <:default as |D|> 110 | {{#if D.isModalOpen}} 111 | 116 |

Stop! Modal Time!

117 |

Without Overlay

118 | 119 |
120 | {{/if}} 121 | 122 |
123 | 124 | 128 | <:menu as |M|> 129 | 130 | 131 | 132 | <:code> 133 | 140 | 141 | 142 | <:default as |D|> 143 | {{#if D.isModalOpen}} 144 | 150 |

Stop! Modal Time!

151 |

Without Overlay - Click Outside to Close

152 | 153 |
154 | {{/if}} 155 | 156 |
157 | 158 | 159 | <:menu as |M|> 160 | 161 | 162 | 163 | <:code> 164 | 171 | 172 | 176 | 177 | 178 | <:default as |D|> 179 | {{#if D.isModalOpen}} 180 | 190 |

Stop! Modal Time!

191 |

Custom Styles

192 | 193 |
194 | {{/if}} 195 | 196 |
197 | 198 | 199 | <:menu as |M|> 200 | 201 | 202 | 203 | <:code> 204 | 208 | 209 | 216 | 217 | 221 | 222 | 223 | <:default as |D|> 224 | {{#if D.isModalOpen}} 225 | 229 |

Stop! Modal Time!

230 |

Via Subclass

231 | 232 |
233 | {{/if}} 234 | 235 |
-------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |
2 |

3 | The modal-dialog component can behave in four different 4 | ways. 5 |

6 | 13 | 14 | {{outlet}} 15 |
-------------------------------------------------------------------------------- /tests/dummy/app/templates/index.hbs: -------------------------------------------------------------------------------- 1 | {{! template-lint-disable no-curly-component-invocation }} 2 |

3 | Basic examples of the 4 | modal-dialog 5 | component 6 |

7 | 8 |

9 | This component uses 10 | ember-wormhole 11 | to relocate the dialog in the DOM to better sit in a layer 12 | above the rest of your document. 13 |

14 |

15 | It requires no additional dependencies because 16 | ember-wormhole 17 | is included with this addon. If 18 | liquid-wormhole 19 | is installed, this component will detect it, and will become 20 | animatable 21 | if 22 | animatable=true 23 | is passed. 24 |

25 | 26 | 27 | <:menu as |M|> 28 | 29 | 30 | 31 | <:code> 32 | 36 | 37 | 38 | <:default as |D|> 39 | {{#if D.isModalOpen}} 40 | 41 |

Stop! Modal Time!

42 |

Basic

43 | 44 |
45 | {{/if}} 46 | 47 |
48 | 49 | 53 | <:menu as |M|> 54 | 55 | 56 | 57 | <:code> 58 | 65 | 66 | 67 | <:default as |D|> 68 | {{#if D.isModalOpen}} 69 | 73 |

Stop! Modal Time!

74 |

With Translucent Overlay

75 | 76 |
77 | {{/if}} 78 | 79 |
80 | 81 | 85 | <:menu as |M|> 86 | 87 | 88 | 89 | <:code> 90 | 97 | 98 | 99 | <:default as |D|> 100 | {{#if D.isModalOpen}} 101 | 106 |

Stop! Modal Time!

107 |

Translucent Overlay with Callback

108 | 109 |
110 | {{/if}} 111 | 112 |
113 | 114 | 118 | <:menu as |M|> 119 | 120 | 121 | 122 | <:code> 123 | 130 | 131 | 132 | <:default as |D|> 133 | {{#if D.isModalOpen}} 134 | 139 |

Stop! Modal Time!

140 |

Without Overlay

141 | 142 |
143 | {{/if}} 144 | 145 |
146 | 147 | 151 | <:menu as |M|> 152 | 153 | 154 | 155 | <:code> 156 | 163 | 164 | 165 | <:default as |D|> 166 | {{#if D.isModalOpen}} 167 | 172 |

Stop! Modal Time!

173 |

With Translucent Overlay as Sibling

174 | 175 |
176 | {{/if}} 177 | 178 |
179 | 180 | 181 | <:menu as |M|> 182 | 183 | 184 | 185 | <:code> 186 | 193 | 194 | 198 | 199 | 200 | <:default as |D|> 201 | {{#if D.isModalOpen}} 202 | 208 |

Stop! Modal Time!

209 |

Custom Styles

210 | 211 |
212 | {{/if}} 213 | 214 |
215 | 216 | 221 | <:menu as |M|> 222 | 223 | 224 | 225 | <:code> 226 | 233 | 234 | 235 | <:default as |D|> 236 | {{#if D.isModalOpen}} 237 | 243 |

Stop! Modal Time!

244 |

Target - Selector: "#alignModalDialogToMe"

245 |

Target Attachment: 246 | {{this.exampleTargetAttachment}}

247 |

Attachment: {{this.exampleAttachment}}

248 | 249 |
250 | {{/if}} 251 | 252 |
253 | 254 | 259 | <:menu as |M|> 260 | 261 | 262 | 263 | 264 | 265 | <:code> 266 | 273 | 274 | 275 | <:default as |D|> 276 | {{#if D.isModalOpen}} 277 | 283 |

Stop! Modal Time!

284 |

Target - Element #bwmde

285 |

Target Attachment: 286 | {{this.exampleTargetAttachment}}

287 |

Attachment: {{this.exampleAttachment}}

288 | 289 |
290 | {{/if}} 291 | 292 |
293 | 294 | 295 | <:menu as |M|> 296 | 297 | 298 | 299 | <:code> 300 | 304 | 305 | 312 | 313 | 317 | 318 | 319 | <:default as |D|> 320 | {{#if D.isModalOpen}} 321 | 325 |

Stop! Modal Time!

326 |

Via Subclass

327 | 328 |
329 | {{/if}} 330 | 331 |
332 | 333 | 334 | <:menu as |M|> 335 | 336 | 337 | 338 | <:code> 339 | 343 | 344 | 351 | 352 | 356 | 357 | 358 | <:default as |D|> 359 | {{#if D.isModalOpen}} 360 | 364 |

Stop! Modal Time!

365 |

Via Subclass

366 | 367 |
368 | {{/if}} 369 | 370 |
371 | 372 | 373 | <:menu as |M|> 374 | 375 | 376 | 377 | <:code> 378 | 385 | 386 | 390 | 391 | 392 | <:default as |D|> 393 |
394 | I AM THE CONTAINER 395 | {{#if D.isModalOpen}} 396 | 403 |

Stop! Modal Time!

404 |

In Place

405 | 406 |
407 | {{/if}} 408 |
409 | 410 |
411 | 412 | 413 | <:menu as |M|> 414 | 415 | 416 | 417 | <:code> 418 | 425 | 426 | 430 | 431 | 432 | <:default as |D|> 433 |
434 | I AM THE CONTAINER 435 | {{#if D.isModalOpen}} 436 | 443 |

Stop! Modal Time!

444 |

In Place

445 | 446 |
447 | {{/if}} 448 |
449 | 450 |
451 | 452 | 458 | <:menu as |M|> 459 | 460 | 461 | 462 | <:code> 463 | 470 | 471 | 475 | 476 | 477 | <:default as |D|> 478 |
479 | {{#if D.isModalOpen}} 480 | 488 |

Really Long Content To Demonstrate Scrolling

489 |
    490 |
  • Hover over modal and scroll
  • 491 |
  • Also hover over overlay and scroll
  • 492 |
493 | {{lorem-ipsum length=30000}} 494 | 495 |
496 | {{/if}} 497 |
498 | 499 |
500 | 501 | 502 | <:menu as |M|> 503 | 504 | 505 | 506 | 507 | 508 | <:code> 509 | 516 | 517 | 518 | <:default as |D|> 519 | {{#if D.isModalOpen}} 520 | 527 |

Centered on element.

528 |
529 | {{/if}} 530 | 531 |
-------------------------------------------------------------------------------- /tests/dummy/app/templates/tethered-animatable.hbs: -------------------------------------------------------------------------------- 1 |

2 | Tethered & animated examples of the 3 | modal-dialog 4 | component 5 |

6 | 7 |

8 | These examples specify a 9 | tetherTarget 10 | property, as well as demonstrate animation via 11 | liquid-fire. If requires that you install 12 | liquid-tether 14 | into your app. Once installed and `animatable=true` is passed, 15 | the component will automatically become animatable using 16 | liquid-fire. 17 |

18 |

19 | If a 20 | tetherTarget 21 | is specified and neither 22 | ember-tether 23 | nor 24 | liquid-tether 25 | is installed, an error will be raised. 26 |

27 | 28 |
29 |

app/transitions.js for these examples

30 | 31 | 38 |
39 | 40 | 41 | <:menu as |M|> 42 | 43 | 44 | 45 | <:code> 46 | 53 | 54 | 58 | 59 | 60 | <:default as |D|> 61 | {{#if D.isModalOpen}} 62 | 72 |

Stop! Modal Time!

73 |

Custom Styles

74 | 75 |
76 | {{/if}} 77 | 78 |
79 | 80 | 85 | <:menu as |M|> 86 | 87 | 88 | 89 | <:code> 90 | 97 | 98 | 99 | <:default as |D|> 100 | {{#if D.isModalOpen}} 101 | 109 |

Stop! Modal Time!

110 |

Target - Selector: "#alignTetherDialogToMe"

111 |

Target Attachment: 112 | {{this.exampleTargetAttachment}}

113 |

Attachment: {{this.exampleAttachment}}

114 | 115 |
116 | {{/if}} 117 | 118 |
119 | 120 | 125 | <:menu as |M|> 126 | 127 | 128 | 129 | 130 | 131 | <:code> 132 | 139 | 140 | 141 | <:default as |D|> 142 | {{#if D.isModalOpen}} 143 | 151 |

Stop! Modal Time!

152 |

Target - Element #bwtde

153 |

Target Attachment: 154 | {{this.exampleTargetAttachment}}

155 |

Attachment: {{this.exampleAttachment}}

156 | 157 |
158 | {{/if}} 159 | 160 |
161 | 162 | 163 | <:menu as |M|> 164 | 165 | 166 | 167 | 168 | 169 | <:code> 170 | 177 | 178 | 179 | <:default as |D|> 180 | {{#if D.isModalOpen}} 181 | 187 |

Centered on element.

188 |
189 | {{/if}} 190 | 191 |
192 | 193 |
194 |

Separate Stacks

195 | 202 | 209 | 216 | 217 |

If you open 1, then 2 (different stacks), #1 should animate 218 | closed, and #2 should animate open.

219 |

If you open 2, then 3, the #2 should animate to #3"s spot.

220 | 221 | 228 | 229 | {{#if this.isShowingSeparateStacksModal1}} 230 | 241 |

I am modal 1.

242 |
243 | {{/if}} 244 | {{#if this.isShowingSeparateStacksModal2}} 245 | 257 |

I am modal 2.

258 |
259 | {{/if}} 260 | {{#if this.isShowingSeparateStacksModal3}} 261 | 273 |

I am modal 3.

274 |
275 | {{/if}} 276 |
-------------------------------------------------------------------------------- /tests/dummy/app/templates/tethered.hbs: -------------------------------------------------------------------------------- 1 |

2 | Tethered examples of the 3 | modal-dialog 4 | component 5 |

6 | 7 |

8 | These examples specify a 9 | tetherTarget 10 | property, which makes the component attempt to use 11 | ember-tether 12 | to be positioned relative to the 13 | tetherTarget 14 | element and sit in a layer above the rest of the document. It 15 | requires that you install 16 | ember-tether 18 | into your app. 19 |

20 |

21 | If 22 | liquid-tether 24 | is installed and `animated=true` is passed, this component 25 | will become 26 | animatable. 27 |

28 |

29 | If a 30 | tetherTarget 31 | is specified and neither 32 | ember-tether 33 | nor 34 | liquid-tether 35 | is installed, an error will be raised. 36 |

37 | 38 | 39 | <:menu as |M|> 40 | 41 | 42 | 43 | <:code> 44 | 51 | 52 | 56 | 57 | 58 | <:default as |D|> 59 | {{#if D.isModalOpen}} 60 | 69 |

Stop! Modal Time!

70 |

Custom Styles

71 | 72 |
73 | {{/if}} 74 | 75 |
76 | 77 | 82 | <:menu as |M|> 83 | 84 | 85 | 86 | <:code> 87 | 94 | 95 | 96 | <:default as |D|> 97 | {{#if D.isModalOpen}} 98 | 105 |

Stop! Modal Time!

106 |

Target - Selector: "#alignTetherDialogToMe"

107 |

Target Attachment: 108 | {{this.exampleTargetAttachment}}

109 |

Attachment: {{this.exampleAttachment}}

110 | 111 |
112 | {{/if}} 113 | 114 |
115 | 116 | 121 | <:menu as |M|> 122 | 123 | 124 | 125 | 126 | 127 | <:code> 128 | 135 | 136 | 137 | <:default as |D|> 138 | {{#if D.isModalOpen}} 139 | 146 |

Stop! Modal Time!

147 |

Target - Element #bwtde

148 |

Target Attachment: 149 | {{this.exampleTargetAttachment}}

150 |

Attachment: {{this.exampleAttachment}}

151 | 152 |
153 | {{/if}} 154 | 155 |
156 | 157 | 158 | <:menu as |M|> 159 | 160 | 161 | 162 | 163 | 164 | <:code> 165 | 172 | 173 | 174 | <:default as |D|> 175 | {{#if D.isModalOpen}} 176 | 181 |

Centered on element.

182 |
183 | {{/if}} 184 | 185 |
-------------------------------------------------------------------------------- /tests/dummy/app/transitions.js: -------------------------------------------------------------------------------- 1 | export default function () { 2 | this.transition( 3 | this.hasClass('liquid-dialog-container'), 4 | this.use( 5 | 'explode', 6 | { 7 | pick: '.ember-modal-overlay', 8 | use: ['fade', { maxOpacity: 0.5 }], 9 | }, 10 | { 11 | pick: '.ember-modal-dialog', 12 | use: ['to-up'], 13 | }, 14 | ), 15 | ); 16 | 17 | this.transition( 18 | this.matchSelector('#modal-overlay'), 19 | this.toValue( 20 | (toValue, fromValue) => toValue === null || fromValue === null, 21 | ), 22 | this.use('fade'), 23 | ); 24 | 25 | this.transition( 26 | this.matchSelector('#modal-dialog'), 27 | this.toValue( 28 | (toValue, fromValue) => toValue !== null && fromValue !== null, 29 | ), 30 | this.use('fly-to'), 31 | ); 32 | 33 | this.transition( 34 | this.matchSelector('#modal-dialog'), 35 | this.toValue( 36 | (toValue, fromValue) => toValue === null || fromValue === null, 37 | ), 38 | this.use('to-up'), 39 | this.reverse('to-down'), 40 | ); 41 | 42 | this.transition( 43 | this.matchSelector('.modal-stack'), 44 | this.toValue( 45 | (toValue, fromValue) => toValue === null || fromValue === null, 46 | ), 47 | this.use('to-up'), 48 | this.reverse('to-down'), 49 | ); 50 | 51 | this.transition( 52 | this.matchSelector('#modal-stack-b'), 53 | this.use('fly-to', { movingSide: 'new' }), 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /tests/dummy/app/utils/code-snippets/animatable.js: -------------------------------------------------------------------------------- 1 | export const codeSnippets = { 2 | 'animated-transitions-js': `this.transition( 3 | this.hasClass('liquid-dialog-container'), 4 | this.use( 5 | 'explode', 6 | { 7 | pick: '.ember-modal-overlay', 8 | use: ['fade', { maxOpacity: 0.5 }], 9 | }, 10 | { 11 | pick: '.ember-modal-dialog', 12 | use: ['to-up'], 13 | }, 14 | ), 15 | );`, 16 | 17 | 'basic-modal-dialog-animatable-hbs': ` 23 |

Stop! Modal Time!

24 |

Basic

25 | 31 |
`, 32 | 33 | 'translucent-modal-dialog-animatable-hbs': ` 40 |

Stop! Modal Time!

41 |

With Translucent Overlay

42 | 48 |
`, 49 | 50 | 'without-overlay-modal-dialog-animatable-hbs': ` 55 |

Stop! Modal Time!

56 |

Without Overlay

57 | 63 |
`, 64 | 65 | 'without-overlay-click-outside-to-close-modal-dialog-animatable-hbs': ` 71 |

Stop! Modal Time!

72 |

Without Overlay - Click Outside to Close

73 | 79 |
`, 80 | 81 | 'custom-styles-modal-dialog-animatable-hbs': ` 91 |

Stop! Modal Time!

92 |

Custom Styles

93 | 99 |
`, 100 | 101 | 'custom-styles-css': `.custom-styles-overlay { 102 | background-color: rgba(15, 157, 88, .77); 103 | } 104 | 105 | .custom-styles-overlay, 106 | .custom-styles-wrapper { 107 | display: flex; 108 | justify-content: flex-end; 109 | align-items: flex-start; 110 | width: 100vw; 111 | height: 100vh; 112 | position: fixed; 113 | top: 0; 114 | left: 0; 115 | } 116 | 117 | .custom-styles-modal-container { 118 | background: #222; 119 | color: white; 120 | margin-left: -25px; 121 | margin-top: 10px; 122 | width: 300px; 123 | 124 | h1 { 125 | color: #0f9d58; 126 | } 127 | }`, 128 | 129 | 'subclass-js': `import ModalDialogComponent from 'ember-modal-dialog/components/modal-dialog'; 130 | 131 | export default class MyCoolModalDialog extends ModalDialogComponent { 132 | translucentOverlay = true; // override default of false 133 | containerClassNames = ['my-cool-modal']; 134 | destinationElementId = 'modal-overlays'; 135 | }`, 136 | 137 | 'subclass-modal-dialog-animatable-hbs': ` 141 |

Stop! Modal Time!

142 |

Via Subclass

143 | 149 |
`, 150 | 151 | 'subclass-styles-css': `.my-cool-modal { 152 | border-radius: 100px; 153 | padding: 40px; 154 | }`, 155 | }; 156 | -------------------------------------------------------------------------------- /tests/dummy/app/utils/code-snippets/index.js: -------------------------------------------------------------------------------- 1 | export const codeSnippets = { 2 | 'basic-modal-dialog-hbs': ` 5 |

Stop! Modal Time!

6 |

Basic

7 | 13 |
`, 14 | 15 | 'translucent-modal-dialog-hbs': ` 19 |

Stop! Modal Time!

20 |

With Translucent Overlay

21 | 27 |
`, 28 | 29 | 'translucent-modal-dialog-with-callback-hbs': ` 34 |

Stop! Modal Time!

35 |

Translucent Overlay with Callback

36 | 42 |
`, 43 | 44 | 'modal-dialog-without-overlay-hbs': ` 49 |

Stop! Modal Time!

50 |

Without Overlay

51 | 57 |
`, 58 | 59 | 'translucent-modal-dialog-sibling-hbs': ` 64 |

Stop! Modal Time!

65 |

With Translucent Overlay as Sibling

66 | 72 |
`, 73 | 74 | 'custom-styles-modal-dialog-hbs': ` 80 |

Stop! Modal Time!

81 |

Custom Styles

82 | 88 |
`, 89 | 90 | 'custom-styles-css': `.custom-styles-overlay { 91 | background-color: rgba(15, 157, 88, .77); 92 | } 93 | 94 | .custom-styles-overlay, 95 | .custom-styles-wrapper { 96 | display: flex; 97 | justify-content: flex-end; 98 | align-items: flex-start; 99 | width: 100vw; 100 | height: 100vh; 101 | position: fixed; 102 | top: 0; 103 | left: 0; 104 | } 105 | 106 | .custom-styles-modal-container { 107 | background: #222; 108 | color: white; 109 | margin-left: -25px; 110 | margin-top: 10px; 111 | width: 300px; 112 | 113 | h1 { 114 | color: #0f9d58; 115 | } 116 | }`, 117 | 118 | 'target-selector-modal-dialog-hbs': ` 124 |

Stop! Modal Time!

125 |

Target - Selector: "#alignModalDialogToMe"

126 |

Target Attachment: {{this.exampleTargetAttachment}}

127 |

Attachment: {{this.exampleAttachment}}

128 | 134 |
`, 135 | 136 | 'target-element-modal-dialog-hbs': ` 142 |

Stop! Modal Time!

143 |

Target - Element #bwmde

144 |

Target Attachment: {{this.exampleTargetAttachment}}

145 |

Attachment: {{this.exampleAttachment}}

146 | 152 |
`, 153 | 154 | 'subclass-js': `import ModalDialogComponent from 'ember-modal-dialog/components/modal-dialog'; 155 | 156 | export default class MyCoolModalDialog extends ModalDialogComponent { 157 | translucentOverlay = true; // override default of false 158 | containerClassNames = ['my-cool-modal']; 159 | destinationElementId = 'modal-overlays'; 160 | }`, 161 | 162 | 'subclass-modal-dialog-animatable-hbs': ` 166 |

Stop! Modal Time!

167 |

Via Subclass

168 | 174 |
`, 175 | 176 | 'subclass-modal-dialog-hbs': ` 180 |

Stop! Modal Time!

181 |

Via Subclass

182 | 188 |
`, 189 | 190 | 'subclass-styles-css': `.my-cool-modal { 191 | border-radius: 100px; 192 | padding: 40px; 193 | }`, 194 | 195 | 'subclass-modal-dialog-2-hbs': ` 199 |

Stop! Modal Time!

200 |

Via Subclass

201 | 207 |
`, 208 | 209 | 'in-place-modal-dialog-hbs': ` 216 |

Stop! Modal Time!

217 |

In Place

218 | 224 |
`, 225 | 226 | 'in-place-css': `.ember-modal-overlay-in-place { 227 | height: initial; 228 | opacity: initial; 229 | position: initial; 230 | } 231 | 232 | .emd-in-place { 233 | position: initial; 234 | }`, 235 | 236 | 'in-place-modal-dialog-2-hbs': ` 243 |

Stop! Modal Time!

244 |

In Place

245 | 251 |
`, 252 | 253 | 'centered-scrolling-modal-dialog-hbs': ` 261 |

Really Long Content To Demonstrate Scrolling

262 | 266 | {{lorem-ipsum length=30000}} 267 | 273 |
`, 274 | 275 | 'centered-scrolling-css': `body.centered-modal-showing { 276 | overflow: hidden; 277 | } 278 | .centered-scrolling-wrapper { 279 | width: 100vw; 280 | height: 100vh; 281 | position: fixed; 282 | top: 0; 283 | left: 0; 284 | overflow-y: scroll; 285 | } 286 | 287 | .centered-scrolling-overlay { 288 | position: relative; 289 | height: auto; 290 | min-height: 100vh; 291 | padding: 1em; 292 | display: flex; 293 | align-items: flex-start; 294 | justify-content: center; 295 | } 296 | 297 | /* basic modal style (an example, this is not necessary for the centering) */ 298 | .centered-scrolling-container { 299 | position: relative; 300 | background-color: white; 301 | min-width: 30em; 302 | max-width: 650px; 303 | min-height: 20em; 304 | padding: 3em; 305 | margin-top: 30px; 306 | margin-bottom: 30px; 307 | box-sizing: border-box; 308 | box-shadow: 0px 4px 25px 4px rgba(0,0,0,0.30); 309 | }`, 310 | 311 | 'element-centered-modal-dialog-hbs': ` 319 |

Centered on element.

320 |
`, 321 | }; 322 | -------------------------------------------------------------------------------- /tests/dummy/app/utils/code-snippets/tethered-animatable.js: -------------------------------------------------------------------------------- 1 | export const codeSnippets = { 2 | 'animated-with-tether-transitions-js': `this.transition( 3 | this.matchSelector('#modal-overlay'), 4 | this.toValue( 5 | (toValue, fromValue) => toValue === null || fromValue === null, 6 | ), 7 | this.use('fade'), 8 | ); 9 | 10 | this.transition( 11 | this.matchSelector('#modal-dialog'), 12 | this.toValue( 13 | (toValue, fromValue) => toValue !== null && fromValue !== null, 14 | ), 15 | this.use('fly-to'), 16 | ); 17 | 18 | this.transition( 19 | this.matchSelector('#modal-dialog'), 20 | this.toValue( 21 | (toValue, fromValue) => toValue === null || fromValue === null, 22 | ), 23 | this.use('to-up'), 24 | this.reverse('to-down'), 25 | ); 26 | 27 | this.transition( 28 | this.matchSelector('.modal-stack'), 29 | this.toValue( 30 | (toValue, fromValue) => toValue === null || fromValue === null, 31 | ), 32 | this.use('to-up'), 33 | this.reverse('to-down'), 34 | ); 35 | 36 | this.transition( 37 | this.matchSelector('#modal-stack-b'), 38 | this.use('fly-to', { movingSide: 'new' }), 39 | );`, 40 | 41 | 'custom-styles-modal-dialog-liquid-tether-hbs': ` 51 |

Stop! Modal Time!

52 |

Custom Styles

53 | 59 |
`, 60 | 61 | 'custom-styles-css': `.custom-styles-overlay { 62 | background-color: rgba(15, 157, 88, .77); 63 | } 64 | 65 | .custom-styles-overlay, 66 | .custom-styles-wrapper { 67 | display: flex; 68 | justify-content: flex-end; 69 | align-items: flex-start; 70 | width: 100vw; 71 | height: 100vh; 72 | position: fixed; 73 | top: 0; 74 | left: 0; 75 | } 76 | 77 | .custom-styles-modal-container { 78 | background: #222; 79 | color: white; 80 | margin-left: -25px; 81 | margin-top: 10px; 82 | width: 300px; 83 | 84 | h1 { 85 | color: #0f9d58; 86 | } 87 | }`, 88 | 89 | 'target-selector-modal-dialog-liquid-tether-hbs': ` 97 |

Stop! Modal Time!

98 |

Target - Selector: "#alignTetherDialogToMe"

99 |

Target Attachment: {{this.exampleTargetAttachment}}

100 |

Attachment: {{this.exampleAttachment}}

101 | 107 |
`, 108 | 109 | 'target-element-modal-dialog-liquid-tether-hbs': ` 117 |

Stop! Modal Time!

118 |

Target - Element #bwtde

119 |

Target Attachment: {{this.exampleTargetAttachment}}

120 |

Attachment: {{this.exampleAttachment}}

121 | 127 |
`, 128 | 129 | 'element-centered-modal-dialog-liquid-tether-hbs': ` 135 |

Centered on element.

136 |
`, 137 | 138 | 'separate-stacks-modal-dialog-liquid-tether-hbs': `{{#if this.isShowingSeparateStacksModal1}} 139 | 150 |

I am modal 1.

151 |
152 | {{/if}} 153 | {{#if this.isShowingSeparateStacksModal2}} 154 | 166 |

I am modal 2.

167 |
168 | {{/if}} 169 | {{#if this.isShowingSeparateStacksModal3}} 170 | 182 |

I am modal 3.

183 |
184 | {{/if}}`, 185 | }; 186 | -------------------------------------------------------------------------------- /tests/dummy/app/utils/code-snippets/tethered.js: -------------------------------------------------------------------------------- 1 | export const codeSnippets = { 2 | 'custom-styles-modal-dialog-tethered-hbs': ` 11 |

Stop! Modal Time!

12 |

Custom Styles

13 | 19 |
`, 20 | 21 | 'custom-styles-css': `.custom-styles-overlay { 22 | background-color: rgba(15, 157, 88, .77); 23 | } 24 | 25 | .custom-styles-overlay, 26 | .custom-styles-wrapper { 27 | display: flex; 28 | justify-content: flex-end; 29 | align-items: flex-start; 30 | width: 100vw; 31 | height: 100vh; 32 | position: fixed; 33 | top: 0; 34 | left: 0; 35 | } 36 | 37 | .custom-styles-modal-container { 38 | background: #222; 39 | color: white; 40 | margin-left: -25px; 41 | margin-top: 10px; 42 | width: 300px; 43 | 44 | h1 { 45 | color: #0f9d58; 46 | } 47 | }`, 48 | 49 | 'target-selector-modal-dialog-tethered-hbs': ` 56 |

Stop! Modal Time!

57 |

Target - Selector: "#alignTetherDialogToMe"

58 |

Target Attachment: {{this.exampleTargetAttachment}}

59 |

Attachment: {{this.exampleAttachment}}

60 | 66 |
`, 67 | 68 | 'target-element-modal-dialog-tethered-hbs': ` 75 |

Stop! Modal Time!

76 |

Target - Element #bwtde

77 |

Target Attachment: {{this.exampleTargetAttachment}}

78 |

Attachment: {{this.exampleAttachment}}

79 | 85 |
`, 86 | 87 | 'element-centered-modal-dialog-tethered-hbs': ` 92 |

Centered on element.

93 |
`, 94 | }; 95 | -------------------------------------------------------------------------------- /tests/dummy/config/deprecation-workflow.js: -------------------------------------------------------------------------------- 1 | /*global self*/ 2 | self.deprecationWorkflow = self.deprecationWorkflow || {}; 3 | self.deprecationWorkflow.config = { 4 | workflow: [ 5 | { 6 | handler: 'silence', 7 | matchId: 'ember-views.curly-components.jquery-element', 8 | }, 9 | { handler: 'silence', matchId: 'computed-property.override' }, 10 | { 11 | handler: 'silence', 12 | matchId: 'deprecated-run-loop-and-computed-dot-access', 13 | }, 14 | { handler: 'silence', matchId: 'ember-global' }, 15 | { handler: 'silence', matchId: 'ember.built-in-components.import' }, 16 | { handler: 'silence', matchId: 'this-property-fallback' }, 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /tests/dummy/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "5.12.0", 7 | "blueprints": [ 8 | { 9 | "name": "addon", 10 | "outputRepo": "https://github.com/ember-cli/ember-addon-output", 11 | "codemodsSource": "ember-addon-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": [ 14 | "--ci-provider=github" 15 | ] 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /tests/dummy/config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup'); 5 | 6 | module.exports = async function () { 7 | return { 8 | usePnpm: true, 9 | scenarios: [ 10 | { 11 | name: 'ember-lts-3.28', 12 | npm: { 13 | devDependencies: { 14 | '@ember/test-helpers': '2.9.4', 15 | 'ember-cli': '~3.28.0', 16 | 'ember-qunit': '6.0.0', 17 | 'ember-resolver': '11.0.1', 18 | 'ember-source': '~3.28.0', 19 | }, 20 | }, 21 | }, 22 | { 23 | name: 'ember-lts-4.12', 24 | npm: { 25 | devDependencies: { 26 | 'ember-source': '~4.12.0', 27 | }, 28 | }, 29 | }, 30 | { 31 | name: 'ember-lts-5.12', 32 | npm: { 33 | devDependencies: { 34 | 'ember-source': '~5.12.0', 35 | }, 36 | }, 37 | }, 38 | { 39 | name: 'ember-release', 40 | allowedToFail: true, 41 | npm: { 42 | devDependencies: { 43 | 'ember-source': await getChannelURL('release'), 44 | }, 45 | }, 46 | }, 47 | { 48 | name: 'ember-beta', 49 | allowedToFail: true, 50 | npm: { 51 | devDependencies: { 52 | 'ember-source': await getChannelURL('beta'), 53 | }, 54 | }, 55 | }, 56 | { 57 | name: 'ember-canary', 58 | allowedToFail: true, 59 | npm: { 60 | devDependencies: { 61 | 'ember-source': await getChannelURL('canary'), 62 | }, 63 | }, 64 | }, 65 | embroiderSafe(), 66 | embroiderOptimized(), 67 | ], 68 | }; 69 | }; 70 | -------------------------------------------------------------------------------- /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: 'history', 9 | EmberENV: { 10 | EXTEND_PROTOTYPES: false, 11 | FEATURES: { 12 | // Here you can enable experimental features on an ember canary build 13 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 14 | }, 15 | }, 16 | 17 | APP: { 18 | // Here you can pass flags/options to your application instance 19 | // when it is created 20 | // Here is how to configure a custom modalRootElementId 21 | // emberModalDialog: { 22 | // modalRootElementId: 'custom-modal-root-element' 23 | //} 24 | }, 25 | 26 | 'ember-shiki': { 27 | defaultThemes: ['github-dark'], 28 | }, 29 | }; 30 | 31 | if (environment === 'development') { 32 | // ENV.APP.LOG_RESOLVER = true; 33 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 34 | // ENV.APP.LOG_TRANSITIONS = true; 35 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 36 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 37 | } 38 | 39 | if (environment === 'test') { 40 | // Testem prefers this... 41 | ENV.locationType = 'none'; 42 | 43 | // keep test console output quieter 44 | ENV.APP.LOG_ACTIVE_GENERATION = false; 45 | ENV.APP.LOG_VIEW_LOOKUPS = false; 46 | 47 | ENV.APP.rootElement = '#ember-testing'; 48 | ENV.APP.autoboot = false; 49 | ENV['ember-tether'] = { 50 | bodyElementId: 'ember-testing', 51 | }; 52 | } 53 | 54 | if (environment === 'production') { 55 | ENV.rootURL = '/ember-modal-dialog'; // for gh-pages live demo 56 | } 57 | 58 | return ENV; 59 | }; 60 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true 6 | } 7 | -------------------------------------------------------------------------------- /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 | module.exports = { 10 | browsers, 11 | }; 12 | -------------------------------------------------------------------------------- /tests/dummy/public/modal-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-modal-dialog/d919ebfaac348f41949b7c74320b28090f05c9fe/tests/dummy/public/modal-dialog.png -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/dummy/public/tether-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-modal-dialog/d919ebfaac348f41949b7c74320b28090f05c9fe/tests/dummy/public/tether-dialog.png -------------------------------------------------------------------------------- /tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-modal-dialog/d919ebfaac348f41949b7c74320b28090f05c9fe/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/helpers/modal-asserts.js: -------------------------------------------------------------------------------- 1 | import { click, waitUntil } from '@ember/test-helpers'; 2 | 3 | export function findContains(selector, text) { 4 | return [].slice 5 | .apply(document.querySelectorAll(selector)) 6 | .filter((e) => e.textContent.trim().indexOf(text) > -1)[0]; 7 | } 8 | 9 | export default function registerAssertHelpers(assert) { 10 | const overlaySelector = '.ember-modal-overlay'; 11 | const dialogSelector = '.ember-modal-dialog'; 12 | 13 | assert.isAbsent = function (selector, message) { 14 | message = message || `${selector} is absent from DOM`; 15 | return this.equal(document.querySelectorAll(selector).length, 0, message); 16 | }; 17 | 18 | assert.isVisible = function (selector, message) { 19 | message = message || `${selector} is not visible`; 20 | return this.dom(selector).isVisible(message); 21 | }; 22 | 23 | assert.hasDataTest = function (selector, dataTest, message) { 24 | message = message || `${selector} has data-test attribute`; 25 | return this.dom(selector).hasAttribute('data-test', dataTest, message); 26 | }; 27 | 28 | assert.dialogOpensAndCloses = async function (options) { 29 | const self = this; 30 | await click(options.openSelector, options.context); 31 | await waitUntil(function () { 32 | return findContains(dialogSelector, options.dialogText); 33 | }); 34 | if (options.hasOverlay) { 35 | self.dom(overlaySelector).exists({ count: 1 }); 36 | } 37 | if (options.whileOpen) { 38 | await options.whileOpen(); 39 | } 40 | await click(options.closeSelector, options.context); 41 | await waitUntil(function () { 42 | return !findContains(dialogSelector, options.dialogText); 43 | }); 44 | self.isAbsent(overlaySelector); 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /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 | 28 | 29 | {{content-for "head-footer"}} 30 | {{content-for "test-head-footer"}} 31 | 32 | 33 | 34 | {{content-for "body"}} 35 | {{content-for "test-body"}} 36 | 37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {{content-for "body-footer"}} 51 | {{content-for "test-body-footer"}} 52 | 53 | 54 | -------------------------------------------------------------------------------- /tests/integration/components/ember-modal-dialog-positioned-container-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 'ember-cli-htmlbars'; 5 | 6 | module( 7 | 'Integration | Component | ember-modal-dialog-positioned-container', 8 | function (hooks) { 9 | setupRenderingTest(hooks); 10 | 11 | test('it renders', async function (assert) { 12 | await render(hbs` 13 | 14 | Hello world! 15 | 16 | `); 17 | 18 | assert.dom().hasText('Hello world!'); 19 | }); 20 | }, 21 | ); 22 | -------------------------------------------------------------------------------- /tests/integration/components/ember-modal-dialog/-basic-dialog-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 'ember-cli-htmlbars'; 5 | 6 | module( 7 | 'Integration | Component | ember-modal-dialog/-basic-dialog', 8 | function (hooks) { 9 | setupRenderingTest(hooks); 10 | 11 | test('it renders', async function (assert) { 12 | await render(hbs` 13 | 14 | Hello world! 15 | 16 | `); 17 | 18 | assert.dom().hasText('Hello world!'); 19 | }); 20 | }, 21 | ); 22 | -------------------------------------------------------------------------------- /tests/integration/components/ember-modal-dialog/-in-place-dialog-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 'ember-cli-htmlbars'; 5 | 6 | module( 7 | 'Integration | Component | ember-modal-dialog/-in-place-dialog', 8 | function (hooks) { 9 | setupRenderingTest(hooks); 10 | 11 | test('it renders', async function (assert) { 12 | await render(hbs` 13 | 14 | Hello world! 15 | 16 | `); 17 | 18 | assert.dom().hasText('Hello world!'); 19 | }); 20 | }, 21 | ); 22 | -------------------------------------------------------------------------------- /tests/integration/components/ember-modal-dialog/-liquid-dialog-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 'ember-cli-htmlbars'; 5 | 6 | module( 7 | 'Integration | Component | ember-modal-dialog/-liquid-dialog', 8 | function (hooks) { 9 | setupRenderingTest(hooks); 10 | 11 | test('it renders', async function (assert) { 12 | await render(hbs` 13 | 14 | Hello world! 15 | 16 | `); 17 | 18 | assert.dom().hasText('Hello world!'); 19 | }); 20 | }, 21 | ); 22 | -------------------------------------------------------------------------------- /tests/integration/components/ember-modal-dialog/-liquid-tether-dialog-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 'ember-cli-htmlbars'; 5 | 6 | module( 7 | 'Integration | Component | ember-modal-dialog/-liquid-tether-dialog', 8 | function (hooks) { 9 | setupRenderingTest(hooks); 10 | 11 | test('it renders', async function (assert) { 12 | await render(hbs` 13 | 16 | Hello world! 17 | 18 | `); 19 | 20 | assert.dom().hasText('Hello world!'); 21 | }); 22 | }, 23 | ); 24 | -------------------------------------------------------------------------------- /tests/integration/components/ember-modal-dialog/-tether-dialog-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 'ember-cli-htmlbars'; 5 | 6 | module( 7 | 'Integration | Component | ember-modal-dialog/-tether-dialog', 8 | function (hooks) { 9 | setupRenderingTest(hooks); 10 | 11 | test('it renders', async function (assert) { 12 | await render(hbs` 13 | 14 | Hello world! 15 | 16 | `); 17 | 18 | assert.dom().hasText('Hello world!'); 19 | }); 20 | }, 21 | ); 22 | -------------------------------------------------------------------------------- /tests/integration/components/ember-modal-dialog/overlay-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 'ember-cli-htmlbars'; 5 | 6 | module( 7 | 'Integration | Component | ember-modal-dialog/overlay', 8 | function (hooks) { 9 | setupRenderingTest(hooks); 10 | 11 | test('it renders', async function (assert) { 12 | await render(hbs` 13 | 14 | `); 15 | 16 | assert.ok(true); 17 | }); 18 | }, 19 | ); 20 | -------------------------------------------------------------------------------- /tests/integration/components/modal-dialog-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 'ember-cli-htmlbars'; 5 | 6 | module('Integration | Component | modal-dialog', function (hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it renders', async function (assert) { 10 | await render(hbs` 11 | 12 | `); 13 | 14 | assert.ok(true); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from 'dummy/app'; 2 | import config from 'dummy/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { start } from 'ember-qunit'; 7 | import registerAssertHelpers from './helpers/modal-asserts'; 8 | 9 | setApplication(Application.create(config.APP)); 10 | 11 | setup(QUnit.assert); 12 | registerAssertHelpers(QUnit.assert); 13 | 14 | start(); 15 | -------------------------------------------------------------------------------- /tests/unit/services/modal-dialog-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupTest } from 'ember-qunit'; 3 | 4 | module('Unit | Service | modal dialog', function (hooks) { 5 | setupTest(hooks); 6 | 7 | test('it knows the destinationElementId', function (assert) { 8 | let service = this.owner.lookup('service:modal-dialog'); 9 | assert.strictEqual(service.get('destinationElementId'), 'modal-overlays'); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yapplabs/ember-modal-dialog/d919ebfaac348f41949b7c74320b28090f05c9fe/vendor/.gitkeep --------------------------------------------------------------------------------