├── .stylelintrc.mjs ├── prettier.config.mjs ├── addon-main.cjs ├── .template-lintrc.cjs ├── .stylelintignore ├── .gitignore ├── demo ├── styles │ └── app.css ├── router.js ├── app.js ├── config.js ├── components │ ├── test-styles.gjs │ ├── test-drag.gjs │ └── test-options.gjs └── templates │ └── application.gjs ├── .env.development ├── index.html ├── config └── ember-cli-update.json ├── .editorconfig ├── .prettierignore ├── src ├── components │ ├── dragula-container.gjs │ └── dragula.gjs └── test-helpers │ └── simulate-drag-drop.js ├── babel.publish.config.cjs ├── CONTRIBUTING.md ├── testem.cjs ├── vite.config.mjs ├── tests ├── index.html ├── test-helper.js └── components │ ├── dragula-container-test.gjs │ └── dragula-test.gjs ├── tsconfig.publish.json ├── LICENSE.md ├── babel.config.cjs ├── .github └── workflows │ └── ci.yml ├── .try.mjs ├── README.md ├── rollup.config.mjs ├── eslint.config.mjs ├── CHANGELOG.md └── package.json /.stylelintrc.mjs: -------------------------------------------------------------------------------- 1 | import zestia from '@zestia/stylelint-config'; 2 | 3 | export default zestia; 4 | -------------------------------------------------------------------------------- /prettier.config.mjs: -------------------------------------------------------------------------------- 1 | import zestia from '@zestia/prettier-config'; 2 | 3 | export default zestia; 4 | -------------------------------------------------------------------------------- /addon-main.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { addonV1Shim } = require('@embroider/addon-shim'); 4 | module.exports = addonV1Shim(__dirname); 5 | -------------------------------------------------------------------------------- /.template-lintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | plugins: ['@zestia/template-lint-config'], 5 | extends: 'zestia:recommended' 6 | }; 7 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | # unconventional files 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | /dist-tests/ 7 | 8 | # addons 9 | /.node_modules.ember-try/ 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | dist/ 3 | dist-tests/ 4 | declarations/ 5 | 6 | # from scenarios 7 | tmp/ 8 | config/optional-features.json 9 | ember-cli-build.js 10 | 11 | # npm/pnpm/yarn pack output 12 | *.tgz 13 | 14 | # deps & caches 15 | node_modules/ 16 | .eslintcache 17 | .prettiercache 18 | -------------------------------------------------------------------------------- /demo/styles/app.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --space: 10px; 3 | } 4 | 5 | .list { 6 | margin-right: var(--space); 7 | } 8 | 9 | .item { 10 | margin: var(--space) auto; 11 | } 12 | 13 | .dragula { 14 | display: flex; 15 | } 16 | 17 | .item.restyle.gu-mirror { 18 | transform: rotate(7deg); 19 | } 20 | -------------------------------------------------------------------------------- /demo/router.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable array-callback-return */ 2 | 3 | import EmberRouter from '@ember/routing/router'; 4 | import config from './config.js'; 5 | 6 | export default class Router extends EmberRouter { 7 | location = config.locationType; 8 | rootURL = config.rootURL; 9 | } 10 | 11 | Router.map(function () {}); 12 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # This file is committed to git and should not contain any secrets. 2 | # 3 | # Vite recommends using .env.local or .env.[mode].local if you need to manage secrets 4 | # SEE: https://vite.dev/guide/env-and-mode.html#env-files for more information. 5 | 6 | 7 | # Default NODE_ENV with vite build --mode=test is production 8 | NODE_ENV=development 9 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | @zestia/dragula 6 | 7 | 8 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "projectName": "@zestia/ember-dragula", 4 | "packages": [ 5 | { 6 | "name": "@ember/addon-blueprint", 7 | "version": "0.9.0", 8 | "blueprints": [ 9 | { 10 | "name": "@ember/addon-blueprint", 11 | "isBaseBlueprint": true, 12 | "options": ["--ci-provider=github", "--npm"] 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | .eslintcache 17 | .lint-todo/ 18 | 19 | # ember-try 20 | /.node_modules.ember-try/ 21 | /bower.json.ember-try 22 | /npm-shrinkwrap.json.ember-try 23 | /package.json.ember-try 24 | /package-lock.json.ember-try 25 | /yarn.lock.ember-try 26 | -------------------------------------------------------------------------------- /src/components/dragula-container.gjs: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import { modifier } from 'ember-modifier'; 3 | 4 | export default class DragulaContainer extends Component { 5 | lifecycle = modifier((element) => { 6 | this.args.onInsert(element); 7 | return () => this.args.onDestroy(element); 8 | }); 9 | 10 | 15 | } 16 | -------------------------------------------------------------------------------- /demo/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import config from './config.js'; 4 | import * as Router from './router.js'; 5 | import * as ApplicationTemplate from './templates/application.gjs'; 6 | import 'dragula/dist/dragula.css'; 7 | 8 | export default class App extends Application { 9 | modulePrefix = config.modulePrefix; 10 | Resolver = Resolver.withModules({ 11 | 'demo/router': Router, 12 | 'demo/templates/application': ApplicationTemplate 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /demo/config.js: -------------------------------------------------------------------------------- 1 | const ENV = { 2 | modulePrefix: 'demo', 3 | environment: import.meta.env.DEV ? 'development' : 'production', 4 | rootURL: '/', 5 | locationType: 'history', 6 | EmberENV: { 7 | EXTEND_PROTOTYPES: false, 8 | FEATURES: { 9 | // Here you can enable experimental features on an ember canary build 10 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 11 | } 12 | }, 13 | APP: { 14 | // Here you can pass flags/options to your application instance 15 | // when it is created 16 | } 17 | }; 18 | 19 | export default ENV; 20 | -------------------------------------------------------------------------------- /babel.publish.config.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * This babel.config is only used for publishing. 5 | * 6 | * For local dev experience, see the babel.config 7 | */ 8 | module.exports = { 9 | plugins: [ 10 | [ 11 | 'babel-plugin-ember-template-compilation', 12 | { 13 | targetFormat: 'hbs', 14 | transforms: [] 15 | } 16 | ], 17 | [ 18 | 'module:decorator-transforms', 19 | { 20 | runtime: { 21 | import: 'decorator-transforms/runtime-esm' 22 | } 23 | } 24 | ] 25 | ], 26 | 27 | generatorOpts: { 28 | compact: false 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | - `git clone ` 6 | - `cd ember-dragula` 7 | - `npm install` 8 | 9 | ## Linting 10 | 11 | - `npm run lint` 12 | - `npm run lint:fix` 13 | 14 | ## Building the addon 15 | 16 | - `npm build` 17 | 18 | ## Running tests 19 | 20 | - `npm run test` – Runs the test suite on the current Ember version 21 | - `npm run test:watch` – Runs the test suite in "watch mode" 22 | 23 | ## Running the test application 24 | 25 | - `npm run start` 26 | - Visit the test application at [http://localhost:4200](http://localhost:4200). 27 | 28 | For more information on using ember-cli, visit [https://cli.emberjs.com/release/](https://cli.emberjs.com/release/). 29 | -------------------------------------------------------------------------------- /testem.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof module !== 'undefined') { 4 | module.exports = { 5 | test_page: 'tests/index.html?hidepassed', 6 | cwd: 'dist-tests', 7 | disable_watching: true, 8 | launch_in_ci: ['Chrome'], 9 | launch_in_dev: ['Chrome'], 10 | browser_start_timeout: 120, 11 | browser_args: { 12 | Chrome: { 13 | ci: [ 14 | // --no-sandbox is needed when running Chrome inside a container 15 | process.env.CI ? '--no-sandbox' : null, 16 | '--headless=new', 17 | '--disable-dev-shm-usage', 18 | '--disable-software-rasterizer', 19 | '--mute-audio', 20 | '--remote-debugging-port=0', 21 | '--window-size=1440,900' 22 | ].filter(Boolean) 23 | } 24 | } 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /vite.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { extensions, ember, classicEmberSupport } from '@embroider/vite'; 3 | import { babel } from '@rollup/plugin-babel'; 4 | 5 | // For scenario testing 6 | const isCompat = Boolean(process.env.ENABLE_COMPAT_BUILD); 7 | 8 | export default defineConfig({ 9 | resolve: { 10 | alias: [ 11 | { 12 | find: '@zestia/ember-dragula', 13 | replacement: `${__dirname}/src` 14 | } 15 | ] 16 | }, 17 | plugins: [ 18 | ...(isCompat ? [classicEmberSupport()] : []), 19 | ember(), 20 | babel({ 21 | babelHelpers: 'inline', 22 | extensions 23 | }) 24 | ], 25 | build: { 26 | rollupOptions: { 27 | input: { 28 | tests: 'tests/index.html' 29 | } 30 | } 31 | }, 32 | define: { 33 | global: 'globalThis' 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | @zestia/ember-dragula Tests 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 | 18 | 21 | 22 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /tsconfig.publish.json: -------------------------------------------------------------------------------- 1 | /** 2 | * This tsconfig is only used for publishing. 3 | * 4 | * For local dev experience, see the tsconfig.json 5 | */ 6 | { 7 | "extends": "@ember/library-tsconfig", 8 | "include": ["./src/**/*", "./unpublished-development-types/**/*"], 9 | "glint": { 10 | "environment": ["ember-loose", "ember-template-imports"] 11 | }, 12 | "compilerOptions": { 13 | "allowJs": true, 14 | "declarationDir": "declarations", 15 | 16 | /** 17 | https://www.typescriptlang.org/tsconfig#rootDir 18 | "Default: The longest common path of all non-declaration input files." 19 | 20 | Because we want our declarations' structure to match our rollup output, 21 | we need this "rootDir" to match the "srcDir" in the rollup.config.mjs. 22 | 23 | This way, we can have simpler `package.json#exports` that matches 24 | imports to files on disk 25 | */ 26 | "rootDir": "./src", 27 | 28 | "types": ["ember-source/types"] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable array-callback-return */ 2 | 3 | import EmberApp from '@ember/application'; 4 | import Resolver from 'ember-resolver'; 5 | import EmberRouter from '@ember/routing/router'; 6 | import * as QUnit from 'qunit'; 7 | import { setApplication } from '@ember/test-helpers'; 8 | import { setup } from 'qunit-dom'; 9 | import { start as qunitStart, setupEmberOnerrorValidation } from 'ember-qunit'; 10 | 11 | class Router extends EmberRouter { 12 | location = 'none'; 13 | rootURL = '/'; 14 | } 15 | 16 | class TestApp extends EmberApp { 17 | modulePrefix = 'test-app'; 18 | Resolver = Resolver.withModules({ 19 | 'test-app/router': { default: Router } 20 | // add any custom services here 21 | }); 22 | } 23 | 24 | Router.map(function () {}); 25 | 26 | export function start() { 27 | setApplication( 28 | TestApp.create({ 29 | autoboot: false, 30 | rootElement: '#ember-testing' 31 | }) 32 | ); 33 | setup(QUnit.assert); 34 | setupEmberOnerrorValidation(); 35 | qunitStart(); 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2025 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 | -------------------------------------------------------------------------------- /babel.config.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * This babel.config is not used for publishing. 5 | * It's only for the local editing experience 6 | * (and linting) 7 | */ 8 | const { buildMacros } = require('@embroider/macros/babel'); 9 | 10 | const { 11 | babelCompatSupport, 12 | templateCompatSupport 13 | } = require('@embroider/compat/babel'); 14 | 15 | const macros = buildMacros(); 16 | 17 | // For scenario testing 18 | const isCompat = Boolean(process.env.ENABLE_COMPAT_BUILD); 19 | 20 | module.exports = { 21 | plugins: [ 22 | [ 23 | 'babel-plugin-ember-template-compilation', 24 | { 25 | transforms: [ 26 | ...(isCompat ? templateCompatSupport() : macros.templateMacros) 27 | ] 28 | } 29 | ], 30 | [ 31 | 'module:decorator-transforms', 32 | { 33 | runtime: { 34 | import: require.resolve('decorator-transforms/runtime-esm') 35 | } 36 | } 37 | ], 38 | ...(isCompat ? babelCompatSupport() : macros.babelMacros) 39 | ], 40 | 41 | generatorOpts: { 42 | compact: false 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /demo/components/test-styles.gjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import Component from '@glimmer/component'; 4 | import Dragula from '@zestia/ember-dragula/components/dragula'; 5 | 6 | export default class TestStylesComponent extends Component { 7 | listOne = [{ name: 'Item 1' }, { name: 'Item 2' }, { name: 'Item 3' }]; 8 | listTwo = [{ name: 'Item 4' }, { name: 'Item 5' }, { name: 'Item 6' }]; 9 | 10 | dropped() { 11 | console.log('Item Dropped'); 12 | } 13 | 14 | dragged() { 15 | console.log('Item Dragged'); 16 | } 17 | 18 | 41 | } 42 | -------------------------------------------------------------------------------- /demo/components/test-drag.gjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import Component from '@glimmer/component'; 4 | import Dragula from '@zestia/ember-dragula/components/dragula'; 5 | 6 | export default class TestDragComponent extends Component { 7 | listOne = [{ name: 'Item 1' }, { name: 'Item 2' }, { name: 'Item 3' }]; 8 | listTwo = [{ name: 'Item 4' }, { name: 'Item 5' }, { name: 'Item 6' }]; 9 | 10 | dropped() { 11 | console.log('Item Dropped'); 12 | } 13 | 14 | dragged() { 15 | console.log('Item Dragged'); 16 | } 17 | 18 | 41 | } 42 | -------------------------------------------------------------------------------- /demo/templates/application.gjs: -------------------------------------------------------------------------------- 1 | import Route from 'ember-route-template'; 2 | import TestDrag from '../components/test-drag'; 3 | import TestOptions from '../components/test-options'; 4 | import TestStyles from '../components/test-styles'; 5 | import '../styles/app.css'; 6 | 7 | export default Route( 8 | 40 | ); 41 | -------------------------------------------------------------------------------- /src/test-helpers/simulate-drag-drop.js: -------------------------------------------------------------------------------- 1 | import { triggerEvent } from '@ember/test-helpers'; 2 | const { floor } = Math; 3 | 4 | export async function simulateDragDrop(elemDrag, elemDrop) { 5 | await simulateDrag(elemDrag); 6 | await simulateDrop(elemDrag, elemDrop); 7 | } 8 | 9 | export async function simulateDrag(elemDrag) { 10 | const rectDrag = elemDrag.getBoundingClientRect(); 11 | const centerDragX = floor(rectDrag.left + rectDrag.width / 2); 12 | const centerDragY = floor(rectDrag.top + rectDrag.height / 2); 13 | const fromOptions = { clientX: centerDragX, clientY: centerDragY }; 14 | const toOptions = { clientX: centerDragX + 1, clientY: centerDragY + 1 }; 15 | 16 | await triggerEvent(elemDrag, 'mousedown', fromOptions); 17 | await triggerEvent(elemDrag, 'mousemove', toOptions); 18 | } 19 | 20 | export async function simulateDrop(elemDrag, elemDrop) { 21 | const rectDrop = elemDrop.getBoundingClientRect(); 22 | const centerDropX = floor(rectDrop.left + rectDrop.width / 2); 23 | const centerDropY = floor(rectDrop.top + rectDrop.height / 2); 24 | const toOptions = { clientX: centerDropX, clientY: centerDropY }; 25 | 26 | await triggerEvent(elemDrag, 'mousemove', toOptions); 27 | await triggerEvent(elemDrag, 'mouseup', toOptions); 28 | } 29 | -------------------------------------------------------------------------------- /tests/components/dragula-container-test.gjs: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import { tracked } from '@glimmer/tracking'; 5 | import DragulaContainer from '@zestia/ember-dragula/components/dragula-container'; 6 | 7 | module('Integration | Component | dragula container', function (hooks) { 8 | setupRenderingTest(hooks); 9 | 10 | test('it sends an action when inserted into the dom', async function (assert) { 11 | assert.expect(1); 12 | 13 | const handleInserted = (element) => 14 | assert.ok(element instanceof HTMLElement); 15 | 16 | const handleDestroyed = () => {}; 17 | 18 | await render( 19 | 25 | ); 26 | }); 27 | 28 | test('it sends a destroy action when removed from the dom', async function (assert) { 29 | assert.expect(1); 30 | 31 | const state = new (class { 32 | @tracked renderComponent = true; 33 | })(); 34 | 35 | const handleInserted = () => {}; 36 | 37 | const handleDestroyed = (element) => 38 | assert.ok(element instanceof HTMLElement); 39 | 40 | await render( 41 | 49 | ); 50 | 51 | state.renderComponent = false; 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /src/components/dragula.gjs: -------------------------------------------------------------------------------- 1 | import Component from '@glimmer/component'; 2 | import DragulaContainer from '@zestia/ember-dragula/components/dragula-container'; 3 | import dragula from 'dragula'; 4 | import { action } from '@ember/object'; 5 | 6 | const { keys } = Object; 7 | 8 | export const events = { 9 | drag: 'onDrag', 10 | dragend: 'onDragEnd', 11 | drop: 'onDrop', 12 | cancel: 'onCancel', 13 | remove: 'onRemove', 14 | shadow: 'onShadow', 15 | over: 'onOver', 16 | out: 'onOut', 17 | cloned: 'onCloned' 18 | }; 19 | 20 | export default class Dragula extends Component { 21 | drake; 22 | 23 | constructor() { 24 | super(...arguments); 25 | 26 | this.drake = dragula({ ...this.args.options }); 27 | this._setupHandlers(); 28 | this.args.onReady?.(this.drake); 29 | } 30 | 31 | @action 32 | addContainer(element) { 33 | this.drake.containers.push(element); 34 | } 35 | 36 | @action 37 | removeContainer(element) { 38 | this.drake.containers.splice(this.drake.containers.indexOf(element), 1); 39 | } 40 | 41 | willDestroy() { 42 | super.willDestroy(...arguments); 43 | this.drake.destroy(); 44 | } 45 | 46 | _setupHandlers() { 47 | keys(events).forEach((name) => { 48 | const handler = this.args[events[name]]; 49 | 50 | if (typeof handler === 'function') { 51 | this.drake.on(name, handler); 52 | } 53 | }); 54 | } 55 | 56 | 70 | } 71 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request: {} 9 | 10 | concurrency: 11 | group: ci-${{ github.head_ref || github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | test: 16 | name: 'Tests' 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 10 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Install Node 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: 18 26 | cache: npm 27 | - name: Install Dependencies 28 | run: npm ci 29 | - name: Lint 30 | run: npm run lint 31 | - name: Run Tests 32 | run: npm run test:ember 33 | 34 | floating: 35 | name: 'Floating Dependencies' 36 | runs-on: ubuntu-latest 37 | timeout-minutes: 10 38 | 39 | steps: 40 | - uses: actions/checkout@v3 41 | - uses: actions/setup-node@v3 42 | with: 43 | node-version: 18 44 | cache: npm 45 | - name: Install Dependencies 46 | run: npm install --no-shrinkwrap 47 | - name: Run Tests 48 | run: npm run test:ember 49 | 50 | try-scenarios: 51 | name: ${{ matrix.try-scenario }} 52 | runs-on: ubuntu-latest 53 | needs: 'test' 54 | timeout-minutes: 10 55 | 56 | strategy: 57 | fail-fast: false 58 | matrix: 59 | try-scenario: 60 | - ember-lts-4.12 61 | - ember-lts-5.4 62 | - ember-release 63 | - ember-beta 64 | - ember-canary 65 | - embroider-safe 66 | - embroider-optimized 67 | 68 | steps: 69 | - uses: actions/checkout@v3 70 | - name: Install Node 71 | uses: actions/setup-node@v3 72 | with: 73 | node-version: 18 74 | cache: npm 75 | - name: Install Dependencies 76 | run: npm ci 77 | - name: Run Tests 78 | run: ./node_modules/.bin/ember try:one ${{ matrix.try-scenario }} 79 | -------------------------------------------------------------------------------- /demo/components/test-options.gjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import Component from '@glimmer/component'; 4 | import Dragula from '@zestia/ember-dragula/components/dragula'; 5 | import { hash } from '@ember/helper'; 6 | 7 | export default class TestOptionsComponent extends Component { 8 | listOne = [{ name: 'Item 1' }, { name: 'Item 2' }, { name: 'Item 3' }]; 9 | listTwo = [{ name: 'Item 4' }, { name: 'Item 5' }, { name: 'Item 6' }]; 10 | 11 | dropped() { 12 | console.log('Item Dropped'); 13 | } 14 | 15 | dragged() { 16 | console.log('Item Dragged'); 17 | } 18 | 19 | moves() { 20 | return false; 21 | } 22 | 23 | 82 | } 83 | -------------------------------------------------------------------------------- /.try.mjs: -------------------------------------------------------------------------------- 1 | // When building your addon for older Ember versions you need to have the required files 2 | const compatFiles = { 3 | 'ember-cli-build.js': `const EmberApp = require('ember-cli/lib/broccoli/ember-app'); 4 | const { compatBuild } = require('@embroider/compat'); 5 | module.exports = async function (defaults) { 6 | const { buildOnce } = await import('@embroider/vite'); 7 | let app = new EmberApp(defaults); 8 | return compatBuild(app, buildOnce); 9 | };`, 10 | 'config/optional-features.json': JSON.stringify({ 11 | 'application-template-wrapper': false, 12 | 'default-async-observers': true, 13 | 'jquery-integration': false, 14 | 'template-only-glimmer-components': true, 15 | 'no-implicit-route-model': true 16 | }) 17 | }; 18 | 19 | const compatDeps = { 20 | '@embroider/compat': '^4.0.3', 21 | 'ember-cli': '^5.12.0', 22 | 'ember-auto-import': '^2.10.0', 23 | '@ember/optional-features': '^2.2.0' 24 | }; 25 | 26 | export default { 27 | scenarios: [ 28 | { 29 | name: 'ember-lts-5.8', 30 | npm: { 31 | devDependencies: { 32 | 'ember-source': '~5.8.0', 33 | ...compatDeps 34 | } 35 | }, 36 | env: { 37 | ENABLE_COMPAT_BUILD: true 38 | }, 39 | files: compatFiles 40 | }, 41 | { 42 | name: 'ember-lts-5.12', 43 | npm: { 44 | devDependencies: { 45 | 'ember-source': '~5.12.0', 46 | ...compatDeps 47 | } 48 | }, 49 | env: { 50 | ENABLE_COMPAT_BUILD: true 51 | }, 52 | files: compatFiles 53 | }, 54 | { 55 | name: `ember-lts-6.4`, 56 | npm: { 57 | devDependencies: { 58 | 'ember-source': `npm:ember-source@~6.4.0` 59 | } 60 | } 61 | }, 62 | { 63 | name: `ember-latest`, 64 | npm: { 65 | devDependencies: { 66 | 'ember-source': `npm:ember-source@latest` 67 | } 68 | } 69 | }, 70 | { 71 | name: `ember-beta`, 72 | npm: { 73 | devDependencies: { 74 | 'ember-source': `npm:ember-source@beta` 75 | } 76 | } 77 | }, 78 | { 79 | name: `ember-alpha`, 80 | npm: { 81 | devDependencies: { 82 | 'ember-source': `npm:ember-source@alpha` 83 | } 84 | } 85 | } 86 | ] 87 | }; 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @zestia/ember-dragula 2 | 3 | 4 | 5 | 6 | [npm-badge]: https://img.shields.io/npm/v/@zestia/ember-dragula.svg 7 | [npm-badge-url]: https://www.npmjs.com/package/@zestia/ember-dragula 8 | [github-actions-badge]: https://github.com/zestia/ember-dragula/workflows/CI/badge.svg 9 | [github-actions-url]: https://github.com/zestia/ember-dragula/actions 10 | [ember-observer-badge]: https://emberobserver.com/badges/-zestia-ember-dragula.svg 11 | [ember-observer-url]: https://emberobserver.com/addons/@zestia/ember-dragula 12 | 13 | This Ember addon provides support for drag and drop using [dragula](https://bevacqua.github.io/dragula/) 14 | 15 | ## Installation 16 | 17 | ``` 18 | ember install @zestia/ember-dragula dragula 19 | ``` 20 | 21 | Add the following to `~/.npmrc` to pull @zestia scoped packages from Github instead of NPM. 22 | 23 | ``` 24 | @zestia:registry=https://npm.pkg.github.com 25 | //npm.pkg.github.com/:_authToken= 26 | ``` 27 | 28 | Add this line to `app/app.js` to import the base `dragula` styles: 29 | 30 | ```js 31 | import 'dragula/dist/dragula.css'; 32 | ``` 33 | 34 | ## Demo 35 | 36 | https://zestia.github.io/ember-dragula 37 | 38 | ## Example 39 | 40 | ```handlebars 41 | 42 | 43 | {{#each this.listOne as |item|}} 44 | {{item}} 45 | {{/each}} 46 | 47 | 48 | 49 | {{#each this.listTwo as |item|}} 50 | {{item}} 51 | {{/each}} 52 | 53 | 54 | ``` 55 | 56 | ## `Dragula` 57 | 58 | ### Arguments 59 | 60 | #### `@options` 61 | 62 | Optional. The full range of options that dragula accepts are supported, see the [docs](https://github.com/bevacqua/dragula#dragulacontainers-options). 63 | 64 | #### `@onReady` 65 | 66 | Optional. The dragula instance is emitted via this action, allowing access to the [`drake`](https://github.com/bevacqua/dragula#api) API. 67 | 68 | #### `@on` 69 | 70 | Optional. The full range of events that dragula emits are supported, see the [docs](https://github.com/bevacqua/dragula#drakeon-events). These can be accessed by prefixing the event name with "on", e.g. `@onDrag` 71 | 72 | ## Test helpers 73 | 74 | To simulate dragging and dropping, test helpers are provided. 75 | 76 |
77 | Example 78 | 79 | ```javascript 80 | import { simulateDragDrop } from '@zestia/ember-dragula/test-helpers/simulate-drag-drop'; 81 | ``` 82 | 83 | Within a test: 84 | 85 | ```javascript 86 | const dragMe = find('.drag-me'); 87 | const dropHere = find('.drop-here'); 88 | 89 | await simulateDrag(dragMe); 90 | await simulateDrop(dragMe, dropHere); 91 | await simulateDragDrop(dragMe, dropHere); 92 | ``` 93 | 94 |
95 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import { babel } from '@rollup/plugin-babel'; 2 | import { Addon } from '@embroider/addon-dev/rollup'; 3 | import { fileURLToPath } from 'node:url'; 4 | import { resolve, dirname } from 'node:path'; 5 | 6 | const addon = new Addon({ 7 | srcDir: 'src', 8 | destDir: 'dist' 9 | }); 10 | 11 | const rootDirectory = dirname(fileURLToPath(import.meta.url)); 12 | const babelConfig = resolve(rootDirectory, './babel.publish.config.cjs'); 13 | 14 | export default { 15 | // This provides defaults that work well alongside `publicEntrypoints` below. 16 | // You can augment this if you need to. 17 | output: addon.output(), 18 | 19 | plugins: [ 20 | // These are the modules that users should be able to import from your 21 | // addon. Anything not listed here may get optimized away. 22 | // By default all your JavaScript modules (**/*.js) will be importable. 23 | // But you are encouraged to tweak this to only cover the modules that make 24 | // up your addon's public API. Also make sure your package.json#exports 25 | // is aligned to the config here. 26 | // See https://github.com/embroider-build/embroider/blob/main/docs/v2-faq.md#how-can-i-define-the-public-exports-of-my-addon 27 | addon.publicEntrypoints(['**/*.js', 'index.js']), 28 | 29 | // These are the modules that should get reexported into the traditional 30 | // "app" tree. Things in here should also be in publicEntrypoints above, but 31 | // not everything in publicEntrypoints necessarily needs to go here. 32 | addon.appReexports([ 33 | 'components/**/*.js', 34 | 'helpers/**/*.js', 35 | 'modifiers/**/*.js', 36 | 'services/**/*.js' 37 | ]), 38 | 39 | // Follow the V2 Addon rules about dependencies. Your code can import from 40 | // `dependencies` and `peerDependencies` as well as standard Ember-provided 41 | // package names. 42 | addon.dependencies(), 43 | 44 | // This babel config should *not* apply presets or compile away ES modules. 45 | // It exists only to provide development niceties for you, like automatic 46 | // template colocation. 47 | // 48 | // By default, this will load the actual babel config from the file 49 | // babel.config.json. 50 | babel({ 51 | extensions: ['.js', '.gjs'], 52 | babelHelpers: 'bundled', 53 | configFile: babelConfig 54 | }), 55 | 56 | // Ensure that standalone .hbs files are properly integrated as Javascript. 57 | addon.hbs(), 58 | 59 | // Ensure that .gjs files are properly integrated as Javascript 60 | addon.gjs(), 61 | 62 | // addons are allowed to contain imports of .css files, which we want rollup 63 | // to leave alone and keep in the published output. 64 | addon.keepAssets(['**/*.css']), 65 | 66 | // Remove leftover build artifacts when starting a new build. 67 | addon.clean() 68 | ] 69 | }; 70 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Debugging: 3 | * https://eslint.org/docs/latest/use/configure/debug 4 | * ---------------------------------------------------- 5 | * 6 | * Print a file's calculated configuration 7 | * 8 | * npx eslint --print-config path/to/file.js 9 | * 10 | * Inspecting the config 11 | * 12 | * npx eslint --inspect-config 13 | * 14 | */ 15 | import babelParser from '@babel/eslint-parser'; 16 | import js from '@eslint/js'; 17 | import prettier from 'eslint-config-prettier'; 18 | import ember from 'eslint-plugin-ember/recommended'; 19 | import importPlugin from 'eslint-plugin-import'; 20 | import n from 'eslint-plugin-n'; 21 | import globals from 'globals'; 22 | import zestia from '@zestia/eslint-config'; 23 | 24 | const esmParserOptions = { 25 | ecmaFeatures: { modules: true }, 26 | ecmaVersion: 'latest' 27 | }; 28 | 29 | const config = [ 30 | js.configs.recommended, 31 | prettier, 32 | ember.configs.base, 33 | ember.configs.gjs, 34 | zestia, 35 | // Temporary 36 | { 37 | rules: { 38 | 'no-restricted-imports': 'off' 39 | } 40 | }, 41 | /** 42 | * Ignores must be in their own object 43 | * https://eslint.org/docs/latest/use/configure/ignore 44 | */ 45 | { 46 | ignores: [ 47 | 'dist/', 48 | 'dist-*/', 49 | 'declarations/', 50 | 'node_modules/', 51 | 'coverage/', 52 | '!**/.*' 53 | ] 54 | }, 55 | /** 56 | * https://eslint.org/docs/latest/use/configure/configuration-files#configuring-linter-options 57 | */ 58 | { 59 | linterOptions: { 60 | reportUnusedDisableDirectives: 'error' 61 | } 62 | }, 63 | { 64 | files: ['**/*.js'], 65 | languageOptions: { 66 | parser: babelParser 67 | } 68 | }, 69 | { 70 | files: ['**/*.{js,gjs}'], 71 | languageOptions: { 72 | parserOptions: esmParserOptions, 73 | globals: { 74 | ...globals.browser 75 | } 76 | } 77 | }, 78 | { 79 | files: ['src/**/*'], 80 | plugins: { 81 | import: importPlugin 82 | }, 83 | rules: { 84 | // require relative imports use full extensions 85 | 'import/extensions': ['error', 'always', { ignorePackages: true }] 86 | } 87 | }, 88 | /** 89 | * CJS node files 90 | */ 91 | { 92 | files: [ 93 | '**/*.cjs', 94 | '.prettierrc.cjs', 95 | '.template-lintrc.cjs', 96 | 'addon-main.cjs' 97 | ], 98 | plugins: { 99 | n 100 | }, 101 | 102 | languageOptions: { 103 | sourceType: 'script', 104 | ecmaVersion: 'latest', 105 | globals: { 106 | ...globals.node 107 | } 108 | } 109 | }, 110 | /** 111 | * ESM node files 112 | */ 113 | { 114 | files: ['**/*.mjs'], 115 | plugins: { 116 | n 117 | }, 118 | 119 | languageOptions: { 120 | sourceType: 'module', 121 | ecmaVersion: 'latest', 122 | parserOptions: esmParserOptions, 123 | globals: { 124 | ...globals.node 125 | } 126 | } 127 | } 128 | ]; 129 | 130 | export default config; 131 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | # 13.0.0 4 | 5 | - Convert to v2 addon 6 | - Removal of `dragula.css` [#31](https://github.com/zestia/ember-dragula/pull/31) 7 | 8 | ## 12.1.4 9 | 10 | - Run ember-cli-update 11 | - Upgrade dependencies 12 | - Convert to `.gjs` 13 | 14 | ## 12.1.3 15 | 16 | - Run ember-cli-update 17 | - Upgrade dependencies 18 | - Convert tests to `.gjs` 19 | 20 | ## 12.1.2 21 | 22 | - Updaate `@zestia` scoped packages 23 | 24 | ## 12.1.1 25 | 26 | - Re-release of 12.1.0 but published to GH Packages instead of NPM 27 | 28 | ## 12.1.0 29 | 30 | - Run `ember-cli-update` 31 | 32 | ## 12.0.0 33 | 34 | - Remove `Ember` prefix from components 35 | - Run `ember-cli-update` 36 | - Yield only a `Container`, not `dragula.Container` 37 | - Upgrade dependencies 38 | 39 | ## 11.0.2 40 | 41 | - Upgrade dependencies 42 | 43 | ## 11.0.1 44 | 45 | - Run `ember-cli-update` 46 | 47 | ## 11.0.0 48 | 49 | - Upgrade dependencies 50 | - Ember Auto Import 2x [#15](https://github.com/zestia/ember-dragula/pull/14) 51 | - Add Embroider support 52 | 53 | ## 10.0.9 54 | 55 | - Upgrade dependencies 56 | 57 | ## 10.0.8 58 | 59 | - Use async/await in test helpers 60 | - Upgrade dependencies 61 | - Run ember-cli-update 62 | 63 | ## 10.0.7 64 | 65 | - Upgrade dependencies 66 | 67 | ## 10.0.6 68 | 69 | - Upgrade dependencies 70 | 71 | ## 10.0.5 72 | 73 | - Upgrade dependencies 74 | 75 | ## 10.0.4 76 | 77 | - Upgrade dependencies 78 | 79 | ## 10.0.3 80 | 81 | - Upgrade dependencies 82 | 83 | ## 10.0.2 84 | 85 | - Upgrade dependencies 86 | 87 | ## 10.0.1 88 | 89 | - Upgrade dependencies 90 | 91 | ## 10.0.0 92 | 93 | - Glimmerise component 94 | - Drop support for Ember < 3.16 95 | 96 | ## 9.0.3 97 | 98 | - Upgrade dependencies 99 | 100 | ## 9.0.2 101 | 102 | - Move render modifiers to dependencies 103 | - Upgrade dependencies 104 | 105 | ## 9.0.1 106 | 107 | - Upgrade dependencies 108 | 109 | ## 9.0.0 110 | 111 | - Small internal refactor 112 | - Drop support for Ember < 3.11 113 | 114 | ## 8.0.1 115 | 116 | - Upgrade dependencies 117 | 118 | ## 8.0.0 119 | 120 | - Rename `@onInit=` action to `@onReady=` 121 | 122 | ## 7.0.0 123 | 124 | - Switch to BEM syntax 125 | 126 | ## 6.0.1 127 | 128 | - Upgrade dependencies 129 | 130 | ## 6.0.0 131 | 132 | - Update templates 133 | - Upgrade dependencies 134 | - Must be invoked using angle brackets for attributes to be forwarded 135 | 136 | ## 5.0.4 137 | 138 | - Upgrade dependencies 139 | 140 | ## 5.0.3 141 | 142 | - Upgrade dependencies 143 | 144 | ## 5.0.2 145 | 146 | - Upgrade dependencies 147 | 148 | ## 5.0.1 149 | 150 | - Correct center drag point calculation in test helpers 151 | 152 | ## 5.0.0 153 | 154 | - Moves location of simulate drag drop test helpers 155 | - Updates drag drop test helpers to use Ember test helpers under the hood 156 | 157 | ## 4.0.0 158 | 159 | - Changes `d.container` to `d.Container` 160 | 161 | ## 3.2.4 162 | 163 | - Upgrade dependencies 164 | 165 | ## 3.2.3 166 | 167 | - Upgrade dependencies 168 | 169 | ## 3.2.2 170 | 171 | - Imports dragula rather than using the global 172 | 173 | ## 3.2.0 174 | 175 | - Changes to camel case actions (`onDragend` -> `onDragEnd`). 176 | 177 | ## < 3.2.0 178 | 179 | - No changelog 180 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zestia/ember-dragula", 3 | "version": "13.0.0", 4 | "description": "An ember addon for dragula support.", 5 | "keywords": [ 6 | "ember-addon", 7 | "dragula", 8 | "ember-dragula", 9 | "sortable" 10 | ], 11 | "repository": "", 12 | "license": "MIT", 13 | "author": "", 14 | "files": [ 15 | "addon-main.cjs", 16 | "declarations", 17 | "dist" 18 | ], 19 | "scripts": { 20 | "build": "rollup --config", 21 | "format": "prettier . --cache --write", 22 | "lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\" --prefixColors auto", 23 | "lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\" --prefixColors auto && npm run format", 24 | "lint:format": "prettier . --cache --check", 25 | "lint:hbs": "ember-template-lint . --no-error-on-unmatched-pattern", 26 | "lint:js": "eslint . --cache", 27 | "lint:hbs:fix": "ember-template-lint . --fix --no-error-on-unmatched-pattern", 28 | "lint:js:fix": "eslint . --fix", 29 | "lint:css": "stylelint '**/*.{css,scss}'", 30 | "lint:css:fix": "concurrently \"npm:lint:css -- --fix\"", 31 | "start": "vite dev", 32 | "test": "vite build --mode=development --out-dir dist-tests && testem --file testem.cjs ci --port 0", 33 | "prepack": "rollup --config", 34 | "release": "release-it" 35 | }, 36 | "dependencies": { 37 | "@embroider/addon-shim": "^1.8.9", 38 | "decorator-transforms": "^2.2.2", 39 | "ember-modifier": "^4.2.0", 40 | "@ember/test-helpers": "^5.2.1", 41 | "dragula": "^3.7.3" 42 | }, 43 | "devDependencies": { 44 | "@babel/core": "^7.25.2", 45 | "@babel/eslint-parser": "^7.25.1", 46 | "@babel/runtime": "^7.25.6", 47 | "@embroider/addon-dev": "^8.1.0", 48 | "@embroider/compat": "^4.1.0", 49 | "@embroider/core": "^4.1.0", 50 | "@embroider/macros": "^1.18.0", 51 | "@embroider/vite": "^1.1.5", 52 | "@eslint/js": "^9.17.0", 53 | "@glimmer/component": "^2.0.0", 54 | "@rollup/plugin-babel": "^6.0.4", 55 | "@zestia/eslint-config": "^7.0.2", 56 | "@zestia/prettier-config": "^1.3.5", 57 | "@zestia/stylelint-config": "^6.1.1", 58 | "@zestia/template-lint-config": "^6.3.0", 59 | "babel-plugin-ember-template-compilation": "^2.2.5", 60 | "concurrently": "^9.0.1", 61 | "ember-qunit": "^9.0.2", 62 | "ember-resolver": "^13.1.0", 63 | "ember-route-template": "^1.0.3", 64 | "ember-source": "^6.3.0", 65 | "ember-template-lint": "^7.9.0", 66 | "eslint": "^9.17.0", 67 | "eslint-config-prettier": "^10.1.5", 68 | "eslint-plugin-ember": "^12.3.3", 69 | "eslint-plugin-import": "^2.31.0", 70 | "eslint-plugin-n": "^17.15.1", 71 | "globals": "^16.1.0", 72 | "prettier": "^3.4.2", 73 | "prettier-plugin-ember-template-tag": "^2.0.4", 74 | "qunit": "^2.24.1", 75 | "qunit-dom": "^3.4.0", 76 | "release-it": "^17.0.1", 77 | "rollup": "^4.22.5", 78 | "testem": "^3.15.1", 79 | "vite": "^6.2.4" 80 | }, 81 | "ember": { 82 | "edition": "octane" 83 | }, 84 | "ember-addon": { 85 | "version": 2, 86 | "type": "addon", 87 | "main": "addon-main.cjs", 88 | "app-js": { 89 | "./components/dragula-container.js": "./dist/_app_/components/dragula-container.js", 90 | "./components/dragula.js": "./dist/_app_/components/dragula.js" 91 | } 92 | }, 93 | "imports": { 94 | "#app/*": "./demo/*", 95 | "#src/*": "./src/*" 96 | }, 97 | "exports": { 98 | ".": "./dist/index.js", 99 | "./*": "./dist/*.js", 100 | "./addon-main.js": "./addon-main.cjs" 101 | }, 102 | "publishConfig": { 103 | "registry": "https://npm.pkg.github.com" 104 | }, 105 | "release-it": { 106 | "hooks": { 107 | "before:init": [ 108 | "npm run lint", 109 | "npm test" 110 | ] 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /tests/components/dragula-test.gjs: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { find, render, rerender } from '@ember/test-helpers'; 4 | import { fn } from '@ember/helper'; 5 | import { tracked } from '@glimmer/tracking'; 6 | import Dragula, { events } from '@zestia/ember-dragula/components/dragula'; 7 | const { keys } = Object; 8 | 9 | module('Integration | Component | dragula', function (hooks) { 10 | setupRenderingTest(hooks); 11 | 12 | test('it renders', async function (assert) { 13 | assert.expect(1); 14 | 15 | await render(); 16 | 17 | assert.dom('.dragula').exists(); 18 | }); 19 | 20 | test('it emits dragula events as actions', async function (assert) { 21 | assert.expect(19); 22 | 23 | const testArgs = ['a', 'b', 'c', 'd']; 24 | 25 | let drake; 26 | 27 | const handleReady = (d) => (drake = d); 28 | 29 | const check = (name, ...args) => { 30 | assert.step(name); 31 | assert.deepEqual(args, testArgs); 32 | }; 33 | 34 | await render( 35 | 49 | ); 50 | 51 | keys(events).forEach((name) => { 52 | drake.emit(name, ...testArgs); 53 | }); 54 | 55 | assert.verifySteps([ 56 | 'drag', 57 | 'dragEnd', 58 | 'drop', 59 | 'cancel', 60 | 'remove', 61 | 'shadow', 62 | 'over', 63 | 'out', 64 | 'cloned' 65 | ]); 66 | }); 67 | 68 | test('it adds container to drake when container is added', async function (assert) { 69 | assert.expect(1); 70 | 71 | let drake; 72 | 73 | const handleReady = (d) => (drake = d); 74 | 75 | await render( 76 | 81 | ); 82 | 83 | assert.deepEqual( 84 | drake.containers[0], 85 | find('.dragula__container:nth-child(1)') 86 | ); 87 | }); 88 | 89 | test('it removes container from drake when container is removed', async function (assert) { 90 | assert.expect(2); 91 | 92 | let drake; 93 | 94 | const state = new (class { 95 | @tracked showContainer = true; 96 | })(); 97 | 98 | const handleReady = (d) => (drake = d); 99 | 100 | await render( 101 | 108 | ); 109 | 110 | assert.deepEqual( 111 | drake.containers[0], 112 | find('.dragula__container:nth-child(1)') 113 | ); 114 | 115 | state.showContainer = false; 116 | 117 | await rerender(); 118 | 119 | assert.deepEqual(drake.containers, []); 120 | }); 121 | 122 | test('tear down', async function (assert) { 123 | assert.expect(2); 124 | 125 | let drake; 126 | 127 | const state = new (class { 128 | @tracked show = true; 129 | })(); 130 | 131 | const handleReady = (d) => (drake = d); 132 | 133 | await render( 134 | 139 | ); 140 | 141 | const originalDrakeDestroy = drake.destroy; 142 | 143 | drake.destroy = () => { 144 | assert.step('destroyed drake'); 145 | originalDrakeDestroy(); 146 | }; 147 | 148 | state.show = false; 149 | 150 | await rerender(); 151 | 152 | assert.verifySteps( 153 | ['destroyed drake'], 154 | 'drake is torn down when the component is torn down' 155 | ); 156 | }); 157 | }); 158 | --------------------------------------------------------------------------------