├── .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 |
11 |
12 | {{yield}}
13 |
14 |
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 |
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 |
19 |
20 | Re-styled
21 |
22 |
23 |
24 |
25 | {{#each this.listOne as |item|}}
26 |
27 | {{item.name}}
28 |
29 | {{/each}}
30 |
31 |
32 |
33 | {{#each this.listTwo as |item|}}
34 |
35 | {{item.name}}
36 |
37 | {{/each}}
38 |
39 |
40 |
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 |
19 |
20 | Basic Drag and Drop
21 |
22 |
23 |
24 |
25 | {{#each this.listOne as |item|}}
26 |
27 | {{item.name}}
28 |
29 | {{/each}}
30 |
31 |
32 |
33 | {{#each this.listTwo as |item|}}
34 |
35 | {{item.name}}
36 |
37 | {{/each}}
38 |
39 |
40 |
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 |
9 |
10 | @zestia/ember-dragula
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | {{outlet}}
26 |
27 | {{! template-lint-disable no-inline-styles }}
28 |
29 |
38 |
39 |
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 |
20 |
24 |
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 |
42 | {{#if state.renderComponent}}
43 |
47 | {{/if}}
48 |
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 |
57 |
58 | {{#let
59 | (component
60 | DragulaContainer
61 | onInsert=this.addContainer
62 | onDestroy=this.removeContainer
63 | )
64 | as |Container|
65 | }}
66 | {{yield Container}}
67 | {{/let}}
68 |
69 |
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 |
24 |
25 | Drag and drop with options
26 |
27 |
28 |
29 | Copy items
30 |
31 |
32 |
38 |
39 | {{#each this.listOne as |item|}}
40 |
41 | {{item.name}}
42 |
43 | {{/each}}
44 |
45 |
46 |
47 | {{#each this.listTwo as |item|}}
48 |
49 | {{item.name}}
50 |
51 | {{/each}}
52 |
53 |
54 |
55 |
56 | Move disabled
57 |
58 |
59 |
65 |
66 | {{#each this.listOne as |item|}}
67 |
68 | {{item.name}}
69 |
70 | {{/each}}
71 |
72 |
73 |
74 | {{#each this.listTwo as |item|}}
75 |
76 | {{item.name}}
77 |
78 | {{/each}}
79 |
80 |
81 |
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 |
36 |
48 |
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 |
77 |
78 |
79 |
80 |
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 |
102 |
103 | {{#if state.showContainer}}
104 |
105 | {{/if}}
106 |
107 |
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 |
135 | {{#if state.show}}
136 |
137 | {{/if}}
138 |
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 |
--------------------------------------------------------------------------------