diff --git a/addon/.eslintignore b/addon/.eslintignore index d474a40b..7658970a 100644 --- a/addon/.eslintignore +++ b/addon/.eslintignore @@ -4,6 +4,7 @@ # compiled output /dist/ +/declarations/ /tmp/ # dependencies diff --git a/addon/.eslintrc.cjs b/addon/.eslintrc.cjs new file mode 100644 index 00000000..cc23797b --- /dev/null +++ b/addon/.eslintrc.cjs @@ -0,0 +1,89 @@ +'use strict'; + +module.exports = { + root: true, + // Only use overrides + // https://github.com/ember-cli/eslint-plugin-ember?tab=readme-ov-file#gtsgjs + overrides: [ + { + files: ['**/*.js', '**/*.ts'], + env: { browser: true }, + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 'latest', + }, + plugins: ['ember', 'import'], + extends: ['eslint:recommended', 'plugin:ember/recommended', 'plugin:prettier/recommended'], + rules: { + // require relative imports use full extensions + 'import/extensions': ['error', 'always', { ignorePackages: true }], + 'ember/no-runloop': 0, + // Add any custom rules here + }, + }, + // ts files + { + files: ['**/*.ts'], + extends: [ + 'eslint:recommended', + 'plugin:ember/recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:prettier/recommended', + ], + rules: { + // require relative imports use full extensions + 'import/extensions': ['error', 'always', { ignorePackages: true }], + 'ember/no-runloop': 0, + // Add any custom rules here + }, + }, + { + files: ['**/*.gts'], + parser: 'ember-eslint-parser', + plugins: ['ember', 'import'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:ember/recommended', + 'plugin:ember/recommended-gts', + 'plugin:prettier/recommended', + ], + rules: { + // require relative imports use full extensions + 'import/extensions': ['error', 'always', { ignorePackages: true }], + // Add any custom rules here + }, + }, + { + files: ['**/*.gjs'], + parser: 'ember-eslint-parser', + plugins: ['ember', 'import'], + extends: [ + 'eslint:recommended', + 'plugin:ember/recommended', + 'plugin:ember/recommended-gjs', + 'plugin:prettier/recommended', + ], + rules: { + // require relative imports use full extensions + 'import/extensions': ['error', 'always', { ignorePackages: true }], + // Add any custom rules here + }, + }, + // node files + { + files: ['./.eslintrc.cjs', './.prettierrc.cjs', './.template-lintrc.cjs', './addon-main.cjs'], + parserOptions: { + sourceType: 'script', + }, + env: { + browser: false, + node: true, + }, + plugins: ['n'], + extends: ['eslint:recommended', 'plugin:n/recommended', 'plugin:prettier/recommended'], + }, + ], +}; diff --git a/addon/.eslintrc.js b/addon/.eslintrc.js deleted file mode 100644 index bb43238e..00000000 --- a/addon/.eslintrc.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -module.exports = { - root: true, - parser: '@babel/eslint-parser', - parserOptions: { - ecmaVersion: 'latest', - sourceType: 'module', - requireConfigFile: false, - babelOptions: { - plugins: [['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }]], - }, - }, - plugins: ['ember'], - extends: ['eslint:recommended', 'plugin:ember/recommended', 'plugin:prettier/recommended'], - env: { - browser: true, - }, - rules: { - 'ember/no-runloop': 0, - }, - overrides: [ - // node files - { - files: [ - './.eslintrc.js', - './.prettierrc.js', - './.template-lintrc.js', - './ember-cli-build.js', - './addon-main.js', - './blueprints/*/index.js', - './config/**/*.js', - ], - parserOptions: { - sourceType: 'script', - }, - env: { - browser: false, - node: true, - }, - extends: ['plugin:n/recommended'], - }, - ], -}; diff --git a/addon/.gitignore b/addon/.gitignore index 5dfef050..b911bfac 100644 --- a/addon/.gitignore +++ b/addon/.gitignore @@ -5,6 +5,7 @@ LICENSE.md # compiled output /dist/ +/declarations/ /tmp/ # dependencies diff --git a/addon/.prettierignore b/addon/.prettierignore index 4178fd57..2d1b8715 100644 --- a/addon/.prettierignore +++ b/addon/.prettierignore @@ -4,6 +4,7 @@ # compiled output /dist/ +/declarations/ /tmp/ # dependencies diff --git a/addon/.prettierrc.js b/addon/.prettierrc.cjs similarity index 100% rename from addon/.prettierrc.js rename to addon/.prettierrc.cjs diff --git a/addon/.template-lintrc.js b/addon/.template-lintrc.cjs similarity index 100% rename from addon/.template-lintrc.js rename to addon/.template-lintrc.cjs diff --git a/addon/addon-main.js b/addon/addon-main.cjs similarity index 86% rename from addon/addon-main.js rename to addon/addon-main.cjs index 9375da06..f868d6b9 100644 --- a/addon/addon-main.js +++ b/addon/addon-main.cjs @@ -1,2 +1,4 @@ +'use strict'; + const { addonV1Shim } = require('@embroider/addon-shim'); module.exports = addonV1Shim(__dirname); diff --git a/addon/babel.config.json b/addon/babel.config.json index cfcf8a3c..2a4b42f9 100644 --- a/addon/babel.config.json +++ b/addon/babel.config.json @@ -1,5 +1,6 @@ { "plugins": [ + ["@babel/plugin-transform-typescript", { "allExtensions": true, "onlyRemoveTypeImports": true, "allowDeclareFields": true }], "@embroider/addon-dev/template-colocation-plugin", ["@babel/plugin-proposal-decorators", { "legacy": true }], "@babel/plugin-proposal-class-properties" diff --git a/addon/package.json b/addon/package.json index c92f93a2..137f4b87 100644 --- a/addon/package.json +++ b/addon/package.json @@ -14,25 +14,48 @@ "test": "tests" }, "exports": { - ".": "./dist/index.js", + ".": { + "types": "./declarations/index.d.ts", + "default": "./dist/index.js" + }, + "./*": { + "types": "./declarations/*.d.ts", + "default": "./dist/*.js" + }, "./_app_/*": "./dist/_app_/*.js", "./modifiers/*": "./dist/modifiers/*.js", "./services/*": "./dist/services/*.js", "./test-support": "./dist/test-support/index.js", - "./addon-main.js": "./addon-main.js" + "./addon-main.js": "./addon-main.cjs" + }, + "typesVersions": { + "*": { + "*": [ + "declarations/*" + ] + } }, "files": [ - "addon-main.js", + "addon-main.cjs", + "declarations", "dist" ], "scripts": { - "build": "rollup --config", - "prepack": "rollup --config", - "lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"", + "build": "concurrently 'pnpm:build:*'", + "build:js": "rollup --config", + "build:types": "glint --declaration", + "lint": "concurrently 'pnpm:lint:*(!fix)' --names 'lint:'", + "lint:fix": "concurrently 'pnpm:lint:*:fix' --names 'fix:'", + "lint:hbs": "ember-template-lint . --no-error-on-unmatched-pattern", + "lint:hbs:fix": "ember-template-lint . --fix --no-error-on-unmatched-pattern", "lint:js": "eslint . --cache", "lint:js:fix": "eslint . --fix", - "start": "rollup --config --watch", - "test": "echo 'Addon does not have tests, run tests in test-app'" + "lint:types": "glint", + "prepack": "concurrently 'pnpm:build:*'", + "start": "concurrently 'pnpm:start:*'", + "start:js": "rollup --config --watch --no-watch.clearScreen", + "start:types": "glint --declaration --watch", + "test": "echo 'A v2 addon does not have tests, run tests in test-app'" }, "peerDependencies": { "ember-modifier": "^3.2.0 || >= 4.0.0", @@ -41,26 +64,62 @@ "ember-source": "^3.28.0 || >= 4.0.0" }, "dependencies": { - "@embroider/addon-shim": "^1.8.9" + "@embroider/addon-shim": "^1.8.9", + "@embroider/macros": "^1.16.6", + "@glimmer/env": "^0.1.7", + "rsvp": "^4.8.5" }, "devDependencies": { "@babel/core": "^7.25.2", + "@babel/eslint-parser": "^7.11.0", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-decorators": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.25.2", "@ember/test-helpers": "^4.0.4", "@ember/test-waiters": "^3.1.0", "@embroider/addon-dev": "^5.0.0", "@glimmer/component": "^1.1.2", + "@glint/core": "^1.4.0", + "@glint/environment-ember-loose": "^1.4.0", + "@glint/environment-ember-template-imports": "^1.4.0", + "@glint/template": "^1.4.0", "@rollup/plugin-babel": "^6.0.4", - "@babel/eslint-parser": "^7.25.1", + "@tsconfig/ember": "^3.0.8", + "@types/ember": "^4.0.11", + "@types/ember__application": "^4.0.11", + "@types/ember__array": "^4.0.10", + "@types/ember__component": "^4.0.22", + "@types/ember__controller": "^4.0.12", + "@types/ember__debug": "^4.0.8", + "@types/ember__engine": "^4.0.11", + "@types/ember__error": "^4.0.6", + "@types/ember__helper": "^4.0.8", + "@types/ember__modifier": "^4.0.9", + "@types/ember__object": "^4.0.12", + "@types/ember__owner": "^4.0.9", + "@types/ember__polyfills": "^4.0.6", + "@types/ember__routing": "^4.0.22", + "@types/ember__runloop": "^4.0.10", + "@types/ember__service": "^4.0.9", + "@types/ember__string": "^3.16.3", + "@types/ember__template": "^4.0.7", + "@types/ember__test": "^4.0.6", + "@types/ember__utils": "^4.0.7", + "@types/ember__destroyable": "^4.0.5", + "@types/rsvp": "^4.0.9", + "@typescript-eslint/eslint-plugin": "^8.3.0", + "@typescript-eslint/parser": "^8.3.0", + "babel-plugin-ember-template-compilation": "^2.2.5", + "concurrently": "^9.0.1", "ember-modifier": "^4.2.0", + "ember-template-lint": "^6.0.0", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-ember": "^12.2.0", + "eslint-plugin-import": "^2.30.0", "eslint-plugin-n": "^17.10.3", "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-qunit": "^8.1.2", - "npm-run-all2": "^6.2.3", "prettier": "^3.3.3", "rollup": "^3.29.5", "rollup-plugin-copy": "^3.5.0", @@ -77,9 +136,9 @@ "edition": "octane" }, "ember-addon": { - "main": "addon-main.js", - "type": "addon", "version": 2, + "type": "addon", + "main": "addon-main.cjs", "demoURL": "https://ember-sortable.netlify.app/", "app-js": { "./modifiers/sortable-group.js": "./dist/_app_/modifiers/sortable-group.js", diff --git a/addon/rollup.config.mjs b/addon/rollup.config.mjs index b819eff3..2efd8f4b 100644 --- a/addon/rollup.config.mjs +++ b/addon/rollup.config.mjs @@ -15,7 +15,12 @@ export default { plugins: [ // These are the modules that users should be able to import from your // addon. Anything not listed here may get optimized away. - addon.publicEntrypoints(['**/*.js']), + addon.publicEntrypoints([ + 'modifiers/**/*.js', + 'services/**/*.js', + 'test-support/**/*.js', + 'utils/**/*.js', + ]), // These are the modules that should get reexported into the traditional // "app" tree. Things in here should also be in publicEntrypoints above, but @@ -29,6 +34,7 @@ export default { // By default, this will load the actual babel config from the file // babel.config.json. babel({ + extensions: ['.js', '.gjs', '.ts', '.gts'], babelHelpers: 'bundled', }), @@ -40,6 +46,9 @@ export default { // Ensure that standalone .hbs files are properly integrated as Javascript. addon.hbs(), + // Ensure that .gjs files are properly integrated as Javascript + addon.gjs(), + // addons are allowed to contain imports of .css files, which we want rollup // to leave alone and keep in the published output. addon.keepAssets(['**/*.css']), diff --git a/addon/src/modifiers/sortable-group.js b/addon/src/modifiers/sortable-group.ts similarity index 79% rename from addon/src/modifiers/sortable-group.js rename to addon/src/modifiers/sortable-group.ts index 680b9471..9ad933e4 100644 --- a/addon/src/modifiers/sortable-group.js +++ b/addon/src/modifiers/sortable-group.ts @@ -10,14 +10,51 @@ import { isRightArrowKey, isSpaceKey, isUpArrowKey, -} from '../utils/keyboard'; -import { ANNOUNCEMENT_ACTION_TYPES } from '../utils/constant'; -import { defaultA11yAnnouncementConfig } from '../utils/defaults'; +} from '../utils/keyboard.ts'; +import { ANNOUNCEMENT_ACTION_TYPES } from '../utils/constant.ts'; +import { defaultA11yAnnouncementConfig, type A11yAnnouncementConfig } from '../utils/defaults.ts'; import { next, schedule, scheduleOnce, later } from '@ember/runloop'; import { inject as service } from '@ember/service'; import { registerDestructor, isDestroyed } from '@ember/destroyable'; +import type { ArgsFor, PositionalArgs, NamedArgs } from 'ember-modifier'; +import type Owner from '@ember/owner'; +import type EmberSortableService from '../services/ember-sortable-internal-state.ts'; +import type { Group } from '../services/ember-sortable-internal-state.ts'; +import type SortableItemModifier from './sortable-item.ts'; +import type { MoveDirection } from './sortable-item.ts'; + +const NO_MODEL: HandleVisualClass = {}; + +export interface HandleVisualClass { + UP?: string; + DOWN?: string; + LEFT?: string; + RIGHT?: string; +} + +interface Position { + x: number; + y: number; +} -const NO_MODEL = {}; +export type TDirection = 'x' | 'y' | 'grid'; + +interface SortableGroupModifierSignature { + Args: { + Named: { + direction?: TDirection; + groupName?: string; + disabled?: boolean; + handleVisualClass?: HandleVisualClass; + a11yAnnouncementConfig?: A11yAnnouncementConfig; + itemVisualClass?: string; + a11yItemName?: string; + onChange: (itemModels: T[], draggedModel: T | undefined) => void; + }; + Positional: unknown[]; + }; + Element: HTMLElement; +} /** * Modifier to apply a11y support to a group container for the Sortable component @@ -39,13 +76,17 @@ const NO_MODEL = {}; * {{/each}} * */ -export default class SortableGroupModifier extends Modifier { +export default class SortableGroupModifier extends Modifier> { /** Primary keyboard utils */ // Tracks the currently selected item - _selectedItem = null; + _selectedItem: SortableItemModifier | null = null; + _group: SortableGroupModifier | null = null; + _firstItemPosition?: Position; + _groupDef!: Group; + // Tracks the current move move = null; - moves = []; + moves: [number, number][] = []; // Tracks the status of keyboard reorder mode isKeyboardReorderModeEnabled = false; @@ -73,7 +114,7 @@ export default class SortableGroupModifier extends Modifier { * RIGHT: 'right', * } */ - get handleVisualClass() { + get handleVisualClass(): HandleVisualClass { return this.named.handleVisualClass || NO_MODEL; } @@ -88,15 +129,15 @@ export default class SortableGroupModifier extends Modifier { * CANCEL: function() {}, * } */ - get a11yAnnouncementConfig() { + get a11yAnnouncementConfig(): A11yAnnouncementConfig { return this.named.a11yAnnouncementConfig || defaultA11yAnnouncementConfig; } - get itemVisualClass() { + get itemVisualClass(): string { return this.named.itemVisualClass || 'is-activated'; } - get a11yItemName() { + get a11yItemName(): string { return this.named.a11yItemName || 'item'; } /** End of a11y properties */ @@ -125,16 +166,21 @@ export default class SortableGroupModifier extends Modifier { * @param {Event} event a DOM event */ @action - keyDown(event) { + keyDown(event: KeyboardEvent) { if (!this.isKeyDownEnabled) { return; } // Note: If handle is specified, we need to target the keyDown on the handle const isKeyboardReorderModeEnabled = this.isKeyboardReorderModeEnabled; - const _selectedItem = this._selectedItem; if (!isKeyboardReorderModeEnabled && (isEnterKey(event) || isSpaceKey(event))) { + const _selectedItem = this._selectedItem; + + if (!_selectedItem) { + return; + } + this._prepareKeyboardReorderMode(); this._announceAction(ANNOUNCEMENT_ACTION_TYPES.ACTIVATE); this._updateItemVisualIndicators(_selectedItem, true); @@ -142,7 +188,7 @@ export default class SortableGroupModifier extends Modifier { this.isRetainingFocus = true; - scheduleOnce('render', () => { + scheduleOnce('render', this, () => { this.element.focus(); this.isRetainingFocus = false; }); @@ -163,8 +209,8 @@ export default class SortableGroupModifier extends Modifier { * * @param {Element} element a DOM element. */ - _isElementWithinHandle(element) { - return element.closest(`[data-sortable-handle]`); + _isElementWithinHandle(element: Element | null): boolean { + return !!element?.closest(`[data-sortable-handle]`); } /** @@ -173,12 +219,16 @@ export default class SortableGroupModifier extends Modifier { * @param {Integer} fromIndex the original index * @param {Integer} toIndex the new index */ - _move(fromIndex, toIndex) { + _move(fromIndex: number, toIndex: number) { const direction = this.direction; const sortedItems = this.sortedItems; const item = sortedItems[fromIndex]; const nextItem = sortedItems[toIndex]; + if (!nextItem || !item) { + return; + } + // switch direction values to notify sortedItems to update, so it sorts by direction. let value; const dimension = direction === 'y' ? 'height' : 'width'; @@ -222,10 +272,14 @@ export default class SortableGroupModifier extends Modifier { * * @param {Event} event a DOM event. */ - _handleKeyboardReorder(event) { + _handleKeyboardReorder(event: KeyboardEvent) { const direction = this.direction; const selectedItem = this._selectedItem; + if (!selectedItem) { + return; + } + if (direction === 'y' && isDownArrowKey(event)) { this.moveItem(selectedItem, 1); } else if (direction === 'y' && isUpArrowKey(event)) { @@ -241,7 +295,7 @@ export default class SortableGroupModifier extends Modifier { this.confirmKeyboardSelection(); this.isRetainingFocus = true; - scheduleOnce('render', () => this._focusItem(itemElement)); + scheduleOnce('render', this, () => this._focusItem(itemElement)); } else if (isEscapeKey(event)) { // cancel will reset the selectedItem, so caching it here before we remove it. const _selectedItemElement = selectedItem.element; @@ -249,12 +303,15 @@ export default class SortableGroupModifier extends Modifier { this.cancelKeyboardSelection(); this.isRetainingFocus = true; - scheduleOnce('render', () => { + scheduleOnce('render', this, () => { const moves = this.moves; - if (moves && moves.length > 0) { + if (moves && moves[0]) { const sortedItems = this.sortedItems; - const itemElement = sortedItems[moves[0].fromIndex].element; - this._focusItem(itemElement); + const fromIndex = moves[0][1]; + const itemElement = sortedItems[fromIndex]?.element; + if (itemElement) { + this._focusItem(itemElement); + } } else { this._focusItem(_selectedItemElement); } @@ -269,7 +326,7 @@ export default class SortableGroupModifier extends Modifier { * @param {SortableItemModifier} item the item to be moved. * @param {Integer} delta how much to move index-wise. */ - moveItem(item, delta) { + moveItem(item: SortableItemModifier, delta: number) { const sortedItems = this.sortedItems; const moves = this.moves; @@ -282,7 +339,7 @@ export default class SortableGroupModifier extends Modifier { } this._announceAction(ANNOUNCEMENT_ACTION_TYPES.MOVE, delta); // Guarantees that the before the UI is fully rendered before we move again. - scheduleOnce('render', () => { + scheduleOnce('render', this, () => { this._move(sortedIndex, newSortedIndex); this._updateHandleVisualIndicators(item, true); @@ -300,12 +357,19 @@ export default class SortableGroupModifier extends Modifier { @action cancelKeyboardSelection() { const _selectedItem = this._selectedItem; + + if (!_selectedItem) { + return; + } + this._disableKeyboardReorderMode(); // Revert the process by reversing the move. const moves = this.moves; while (moves.length > 0) { const move = moves.pop(); - this._move(move[1], move[0]); + const fromIndex = move ? move[1] : 0; + const toIndex = move ? move[0] : 0; + this._move(fromIndex, toIndex); } this._tearDownA11yApplicationContainer(); this._updateItemVisualIndicators(_selectedItem, false); @@ -323,6 +387,11 @@ export default class SortableGroupModifier extends Modifier { */ confirmKeyboardSelection() { const _selectedItem = this._selectedItem; + + if (!_selectedItem) { + return; + } + this.moves = []; this._disableKeyboardReorderMode(); this._tearDownA11yApplicationContainer(); @@ -339,7 +408,7 @@ export default class SortableGroupModifier extends Modifier { * @param {String} type the action type. * @param {Number} delta how much distance (item-wise) is being moved. */ - _announceAction(type, delta = null) { + _announceAction(type: keyof A11yAnnouncementConfig, delta: number = 0): void { const a11yAnnouncementConfig = this.a11yAnnouncementConfig; const a11yItemName = this.a11yItemName; @@ -349,9 +418,18 @@ export default class SortableGroupModifier extends Modifier { const sortedItems = this.sortedItems; const _selectedItem = this._selectedItem; + + if (!_selectedItem) { + return; + } + const index = sortedItems.indexOf(_selectedItem); const announcer = this.announcer; + if (!announcer) { + return; + } + const config = { a11yItemName, index: index, @@ -372,7 +450,7 @@ export default class SortableGroupModifier extends Modifier { /** * Reset the selected item. */ - _resetItemSelection() { + _resetItemSelection(): void { this._selectedItem = null; } @@ -382,7 +460,7 @@ export default class SortableGroupModifier extends Modifier { * @param {SortableItemModifier} item the selected item. * @param {Boolean} isActive to activate or deactivate the class. */ - _updateItemVisualIndicators(item, isActive) { + _updateItemVisualIndicators(item: SortableItemModifier, isActive: boolean) { const itemVisualClass = this.itemVisualClass; if (!itemVisualClass || !item) { @@ -402,7 +480,7 @@ export default class SortableGroupModifier extends Modifier { * @param {SortableItemModifier} item the selected item. * @param {boolean} isUpdate to update or not update. */ - _updateHandleVisualIndicators(item, isUpdate) { + _updateHandleVisualIndicators(item: SortableItemModifier, isUpdate: boolean) { const handleVisualClass = this.handleVisualClass; if (handleVisualClass === NO_MODEL || !item) { @@ -414,22 +492,22 @@ export default class SortableGroupModifier extends Modifier { const index = sortedItems.indexOf(item); const handle = item.element.querySelector('[data-sortable-handle'); const visualHandle = handle ? handle : item.element; - const visualKeys = direction === 'y' ? ['UP', 'DOWN'] : ['LEFT', 'RIGHT']; + const visualKeys: (keyof HandleVisualClass)[] = direction === 'y' ? ['UP', 'DOWN'] : ['LEFT', 'RIGHT']; visualKeys.forEach((visualKey) => { - visualHandle.classList.remove(handleVisualClass[visualKey]); + visualHandle.classList.remove(handleVisualClass[visualKey] ?? ''); }); if (!isUpdate) { return; } - if (index > 0) { - visualHandle.classList.add(handleVisualClass[visualKeys[0]]); + if (index > 0 && visualKeys[0]) { + visualHandle.classList.add(handleVisualClass[visualKeys[0]] ?? ''); } - if (index < sortedItems.length - 1) { - visualHandle.classList.add(handleVisualClass[visualKeys[1]]); + if (index < sortedItems.length - 1 && visualKeys[1]) { + visualHandle.classList.add(handleVisualClass[visualKeys[1]] ?? ''); } } @@ -438,8 +516,8 @@ export default class SortableGroupModifier extends Modifier { * * @param {Element} itemElement an DOM element representing an sortable-item. */ - _focusItem(itemElement) { - const handle = itemElement.querySelector('[data-sortable-handle]'); + _focusItem(itemElement: HTMLElement): void { + const handle = itemElement.querySelector('[data-sortable-handle]') as HTMLElement | null; if (handle) { handle.focus(); } else { @@ -451,21 +529,21 @@ export default class SortableGroupModifier extends Modifier { /** * Enables keyboard reorder mode. */ - _enableKeyboardReorderMode() { + _enableKeyboardReorderMode(): void { this.isKeyboardReorderModeEnabled = true; } /** * Disables keyboard reorder mode */ - _disableKeyboardReorderMode() { + _disableKeyboardReorderMode(): void { this.isKeyboardReorderModeEnabled = false; } /** * Sets up the group as an application and make it programmatically focusable. */ - _setupA11yApplicationContainer() { + _setupA11yApplicationContainer(): void { this.element.setAttribute('role', 'application'); this.element.tabIndex = -1; } @@ -473,12 +551,12 @@ export default class SortableGroupModifier extends Modifier { /** * Tears down the `role=application` container. */ - _tearDownA11yApplicationContainer() { + _tearDownA11yApplicationContainer(): void { this.element.removeAttribute('role'); this.element.removeAttribute('tabIndex'); } - _prepareKeyboardReorderMode() { + _prepareKeyboardReorderMode(): void { this._enableKeyboardReorderMode(); this._setupA11yApplicationContainer(); } @@ -508,7 +586,7 @@ export default class SortableGroupModifier extends Modifier { } @service('ember-sortable-internal-state') - sortableService; + declare sortableService: EmberSortableService; /** * This is the group name used to keep groups separate if there are more than one on the screen at a time. @@ -517,7 +595,7 @@ export default class SortableGroupModifier extends Modifier { * @default "_EmberSortableGroup" * @returns {*|string} */ - get groupName() { + get groupName(): string { return this.named.groupName || '_EmberSortableGroup'; } @@ -527,10 +605,10 @@ export default class SortableGroupModifier extends Modifier { @property items @type SortableItemModifier[] */ - get items() { + get items(): SortableItemModifier[] { return this._groupDef.items; } - set(items) { + set(items: SortableItemModifier[]) { this._groupDef.items = items; } @@ -539,7 +617,7 @@ export default class SortableGroupModifier extends Modifier { * * @type {Element} */ - announcer = null; + announcer: Element | null = null; /** Position for the first item. @@ -548,11 +626,18 @@ export default class SortableGroupModifier extends Modifier { @type Number */ @computed('direction', 'sortedItems') - get firstItemPosition() { + get firstItemPosition(): Position { const sortedItems = this.sortedItems; const item = sortedItems[0]; + if (!item) { + return { + x: 0, + y: 0, + }; + } + return { x: item.x - item.spacing, y: item.y - item.spacing, @@ -564,7 +649,7 @@ export default class SortableGroupModifier extends Modifier { @property sortedItems @type SortableItemModifier[] */ - get sortedItems() { + get sortedItems(): SortableItemModifier[] { const direction = this.direction; const groupStyles = getComputedStyle(this.element); @@ -572,7 +657,7 @@ export default class SortableGroupModifier extends Modifier { return this.items.sort((a, b) => { if (direction === 'grid') { - let { ax, ay, bx, by } = this._calculateGridPosition(a, b, groupWidth); + const { ax, ay, bx, by } = this._calculateGridPosition(a, b, groupWidth); if (ay == by) return ax - bx; return ay - by; } @@ -584,7 +669,7 @@ export default class SortableGroupModifier extends Modifier { * Enables keyboard navigation */ @action - activateKeyDown(selectedItem) { + activateKeyDown(selectedItem: SortableItemModifier): void { this._selectedItem = selectedItem; this.isKeyDownEnabled = true; } @@ -596,7 +681,7 @@ export default class SortableGroupModifier extends Modifier { * by ignoring them. */ @action - deactivateKeyDown() { + deactivateKeyDown(): void { this.isKeyDownEnabled = false; } @@ -606,7 +691,7 @@ export default class SortableGroupModifier extends Modifier { @param {SortableGroupModifier} group */ @action - registerGroup(group) { + registerGroup(group: SortableGroupModifier): void { this._group = group; } @@ -616,7 +701,7 @@ export default class SortableGroupModifier extends Modifier { @param {SortableGroupModifier} group */ @action - deregisterGroup(group) { + deregisterGroup(group: SortableGroupModifier) { if (this._group === group) { this._group = null; } @@ -639,7 +724,7 @@ export default class SortableGroupModifier extends Modifier { @param {SortableItemModifier[]} sortedItems */ @action - update(sortedItems) { + update(sortedItems: SortableItemModifier[]): void { if (!sortedItems) { sortedItems = this.sortedItems; } @@ -652,7 +737,7 @@ export default class SortableGroupModifier extends Modifier { axis = this.firstItemPosition; } - let direction = this.direction; + const direction = this.direction; let position = 0; let groupPositionRight = 0; @@ -689,23 +774,19 @@ export default class SortableGroupModifier extends Modifier { position += item.spacing * 2; } - let dimension; - if (direction === 'grid') { - dimension = 'width'; - if (item.height > maxPrevHeight) { maxPrevHeight = item.height; } + + position += item.width; } if (direction === 'x') { - dimension = 'width'; + position += item.height; } if (direction === 'y') { - dimension = 'height'; + position += item.height; } - - position += item[dimension]; }); } @@ -713,7 +794,7 @@ export default class SortableGroupModifier extends Modifier { @method _commit */ @action - commit() { + commit(): void { const items = this.sortedItems; const itemModels = items.map((item) => item.model); const draggedItem = items.find((item) => item.wasDropped); @@ -729,7 +810,7 @@ export default class SortableGroupModifier extends Modifier { } @action - _onChange(itemModels, draggedModel) { + _onChange(itemModels: T[], draggedModel: T | undefined): void { if (this.onChange) { this.onChange(itemModels, draggedModel); } @@ -739,7 +820,7 @@ export default class SortableGroupModifier extends Modifier { * Keeps the UI in sync with actual changes. * Needed for drag and keyboard operations. */ - _updateItems() { + _updateItems(): void { const items = this.sortedItems; delete this._firstItemPosition; @@ -760,16 +841,25 @@ export default class SortableGroupModifier extends Modifier { } @action - _createAnnouncer() { + _createAnnouncer(): HTMLSpanElement { const announcer = document.createElement('span'); announcer.setAttribute('aria-live', 'polite'); announcer.classList.add('visually-hidden'); return announcer; } - _calculateGridPosition(a, b, groupWidth) { - const groupTopPos = a.element.parentNode?.offsetTop ?? 0; - const groupLeftPos = a.element.parentNode?.offsetLeft ?? 0; + _calculateGridPosition( + a: SortableItemModifier, + b: SortableItemModifier, + groupWidth: number, + ): { + ax: number; + ay: number; + bx: number; + by: number; + } { + const groupTopPos = (a.element.parentNode as HTMLElement | null)?.offsetTop ?? 0; + const groupLeftPos = (a.element.parentNode as HTMLElement | null)?.offsetLeft ?? 0; const position = { ax: a.x, @@ -825,7 +915,18 @@ export default class SortableGroupModifier extends Modifier { return position; } - _calculateGridDragItemPos(x, y, otherX, otherY, width, height, moveDirection, groupTopPos, groupLeftPos, groupWidth) { + _calculateGridDragItemPos( + x: number, + y: number, + otherX: number, + otherY: number, + width: number, + height: number, + moveDirection: MoveDirection, + groupTopPos: number, + groupLeftPos: number, + groupWidth: number, + ): Position { const toleranceWidth = width / 4; const initialX = x; @@ -874,25 +975,30 @@ export default class SortableGroupModifier extends Modifier { // end of API - addEventListener() { + addEventListener(): void { this.element.addEventListener('keydown', this.keyDown); this.element.addEventListener('focusout', this.focusOut); } - removeEventListener() { + removeEventListener(): void { this.element.removeEventListener('keydown', this.keyDown); this.element.removeEventListener('focusout', this.focusOut); } - element; + element!: HTMLElement; didSetup = false; + named!: NamedArgs>; - constructor(owner, args) { + constructor(owner: Owner, args: ArgsFor>) { super(owner, args); registerDestructor(this, cleanup); } - modify(element, _positional, named) { + override modify( + element: HTMLElement, + _positional: PositionalArgs>, + named: NamedArgs>, + ) { this.element = element; this.named = named; @@ -919,11 +1025,11 @@ export default class SortableGroupModifier extends Modifier { * * @param {SortableGroupModifier} instance */ -function cleanup(instance) { +function cleanup(instance: SortableGroupModifier) { // todo cleanup the announcer - if (instance.announcer.parentNode) { + if (instance.announcer?.parentNode) { instance.announcer.parentNode.removeChild(instance.announcer); } instance.removeEventListener(); - instance.sortableService.deregisterGroup(instance.groupName, instance); + instance.sortableService.deregisterGroup(instance.groupName); } diff --git a/addon/src/modifiers/sortable-handle.js b/addon/src/modifiers/sortable-handle.ts similarity index 69% rename from addon/src/modifiers/sortable-handle.js rename to addon/src/modifiers/sortable-handle.ts index e2ad412a..608ced87 100644 --- a/addon/src/modifiers/sortable-handle.js +++ b/addon/src/modifiers/sortable-handle.ts @@ -1,5 +1,9 @@ import Modifier from 'ember-modifier'; +interface SortableHandleModifierSignature { + Element: HTMLElement; +} + /** * Modifier to to mark the handle of an item. If this is not supplied the item will be the handle * @@ -14,14 +18,14 @@ import Modifier from 'ember-modifier'; * {{/each}} * */ -export default class SortableHandleModifier extends Modifier { +export default class SortableHandleModifier extends Modifier { didSetup = false; - modify(element /*, positional, named*/) { + override modify(element: HTMLElement /*, positional, named*/) { if (!this.didSetup) { // take the model and look up the registered element, the tell that element you are the handle - element.dataset.sortableHandle = true; - element.tabIndex = '0'; + element.dataset['sortableHandle'] = 'true'; + element.setAttribute('tabIndex', '0'); element.setAttribute('role', 'button'); this.didSetup = true; } diff --git a/addon/src/modifiers/sortable-item.js b/addon/src/modifiers/sortable-item.ts similarity index 70% rename from addon/src/modifiers/sortable-item.js rename to addon/src/modifiers/sortable-item.ts index 65fadda3..1d211f83 100644 --- a/addon/src/modifiers/sortable-item.js +++ b/addon/src/modifiers/sortable-item.ts @@ -2,21 +2,69 @@ import Modifier from 'ember-modifier'; import { Promise, defer } from 'rsvp'; import { action, set } from '@ember/object'; -import { DRAG_ACTIONS, ELEMENT_CLICK_ACTION, END_ACTIONS } from '../utils/constant'; +import { DRAG_ACTIONS, ELEMENT_CLICK_ACTION, END_ACTIONS } from '../utils/constant.ts'; import { run, throttle, bind, scheduleOnce, later } from '@ember/runloop'; import { DEBUG } from '@glimmer/env'; -import { getX, getY } from '../utils/coordinate'; -import ScrollContainer from '../system/scroll-container'; -import scrollParent from '../system/scroll-parent'; -import { getBorderSpacing } from '../utils/css-calculation'; +import { getX, getY } from '../utils/coordinate.ts'; +import ScrollContainer from '../system/scroll-container.ts'; +import scrollParent from '../system/scroll-parent.ts'; +import { getBorderSpacing } from '../utils/css-calculation.ts'; import { buildWaiter } from '@ember/test-waiters'; import { inject as service } from '@ember/service'; import { assert, deprecate } from '@ember/debug'; import { registerDestructor } from '@ember/destroyable'; import { isTesting } from '@embroider/macros'; +import type { ArgsFor, PositionalArgs, NamedArgs } from 'ember-modifier'; +import type EmberSortableService from '../services/ember-sortable-internal-state.ts'; +import type Owner from '@ember/owner'; +import type { Group } from '../services/ember-sortable-internal-state.ts'; +import type SortableGroupModifier from './sortable-group.ts'; +import type { TDirection } from './sortable-group.ts'; const sortableItemWaiter = buildWaiter('sortable-item-waiter'); +export interface MoveDirection { + left: boolean; + right: boolean; + top: boolean; + bottom: boolean; +} + +interface ItemContainer { + width: number; + readonly height: number; + readonly left: number; + readonly right: number; + readonly top: number; + readonly bottom: number; +} + +export interface FakeEvent { + pageX: number; + pageY: number; + clientX: number; + clientY: number; +} + +export interface SortableItemModifierSignature { + Args: { + Named: { + model: T; + groupName?: string; + disabled?: boolean; + updateInterval?: number; + spacing?: number; + isDraggingDisabled?: boolean; + handle?: string; + distance?: number; + disableCheckScrollBounds?: boolean; + onDragStart?: (item: T) => void; + onDragStop?: (item: T) => void; + }; + }; + Element: HTMLElement; +} + /** * Modifier to mark an element as an item to be reordered * @@ -36,19 +84,28 @@ const sortableItemWaiter = buildWaiter('sortable-item-waiter'); * {{/each}} * */ -export default class SortableItemModifier extends Modifier { +export default class SortableItemModifier extends Modifier> { className = 'sortable-item'; - @service('ember-sortable-internal-state') sortableService; + @service('ember-sortable-internal-state') declare sortableService: EmberSortableService; + + startEvent?: FakeEvent | Event; + + _sortableGroup?: Group; + _x?: number; + _y?: number; + _dragOriginX?: number; + _dragOriginY?: number; + _pageX?: number; + _pageY?: number; - _sortableGroup; /** * The SortableGroupModifier this item belongs to. Assigned by the group * when it inspects all the items in the list * * @type SortableGroupModifier */ - get sortableGroup() { + get sortableGroup(): SortableGroupModifier { if (this._sortableGroup === undefined) { this._sortableGroup = this.sortableService.fetchGroup(this.groupName); assert( @@ -56,18 +113,18 @@ export default class SortableItemModifier extends Modifier { this._sortableGroup !== undefined, ); } - return this._sortableGroup.groupModifier; + return this._sortableGroup.groupModifier!; } - get model() { + get model(): T { return this.named.model; } - get direction() { + get direction(): TDirection { return this.sortableGroup?.direction; } - get groupDisabled() { + get groupDisabled(): boolean { return this.sortableGroup?.disabled; } @@ -76,9 +133,9 @@ export default class SortableItemModifier extends Modifier { * If no group is assigned a default is used * * @default "_EmberSortableGroup" - * @returns {*|string} + * @returns {string} */ - get groupName() { + get groupName(): string { return this.named.groupName || '_EmberSortableGroup'; } @@ -89,7 +146,7 @@ export default class SortableItemModifier extends Modifier { @type Number @default 125 */ - get updateInterval() { + get updateInterval(): number { return this.named.updateInterval || 125; } @@ -99,7 +156,7 @@ export default class SortableItemModifier extends Modifier { @type Number @default 0[px] */ - get spacing() { + get spacing(): number { return this.named.spacing || 0; } @@ -109,7 +166,7 @@ export default class SortableItemModifier extends Modifier { @type boolean @default false */ - get isDisabled() { + get isDisabled(): boolean { deprecate( '"isDraggingDisabled" is deprecated. Please migrate to "disabled" named argument', !('isDraggingDisabled' in this.named), @@ -143,7 +200,7 @@ export default class SortableItemModifier extends Modifier { return this.named.handle || '[data-sortable-handle]'; } - handleElement; + handleElement?: HTMLElement | null; /** * Tolerance, in pixels, for when sorting should start. @@ -155,7 +212,7 @@ export default class SortableItemModifier extends Modifier { * @type Integer * @default 0 */ - get distance() { + get distance(): number { return this.named.distance || 0; } @@ -186,7 +243,7 @@ export default class SortableItemModifier extends Modifier { * @property moveDirection * @type Object */ - get moveDirection() { + get moveDirection(): MoveDirection { const moveDirection = { left: false, right: false, @@ -201,19 +258,19 @@ export default class SortableItemModifier extends Modifier { const dragOriginX = this._dragOriginX; const dragOriginY = this._dragOriginY; - if (dragOriginX > this._pageX) { + if (dragOriginX && this._pageX && dragOriginX > this._pageX) { moveDirection.left = true; } - if (dragOriginX < this._pageX) { + if (dragOriginX && this._pageX && dragOriginX < this._pageX) { moveDirection.right = true; } - if (dragOriginY > this._pageY) { + if (dragOriginY && this._pageY && dragOriginY > this._pageY) { moveDirection.top = true; } - if (dragOriginY < this._pageY) { + if (dragOriginY && this._pageY && dragOriginY < this._pageY) { moveDirection.bottom = true; } @@ -227,8 +284,8 @@ export default class SortableItemModifier extends Modifier { @param {Object} item model @default null */ - get onDragStart() { - return this.named.onDragStart || ((item) => item); + get onDragStart(): (item: T) => void { + return this.named.onDragStart || ((item: T) => item); } /** @@ -238,8 +295,8 @@ export default class SortableItemModifier extends Modifier { @param {Object} item model @default null */ - get onDragStop() { - return this.named.onDragStop || ((item) => item); + get onDragStop(): (item: T) => void { + return this.named.onDragStop || ((item: T) => item); } /** @@ -249,10 +306,10 @@ export default class SortableItemModifier extends Modifier { @default false */ _isDropping = false; - get isDropping() { + get isDropping(): boolean { return this._isDropping; } - set isDropping(value) { + set isDropping(value: boolean) { if (value) { this.element.classList.add('is-dropping'); } else { @@ -273,22 +330,22 @@ export default class SortableItemModifier extends Modifier { @property isBusy @type Boolean */ - get isBusy() { + get isBusy(): boolean { return this.isDragging || this.isDropping; } /** @property disableCheckScrollBounds */ - get disableCheckScrollBounds() { - return this.named.disableCheckScrollBounds != undefined ? this.named.disableCheckScrollBounds : isTesting(); + get disableCheckScrollBounds(): boolean { + return this.named.disableCheckScrollBounds !== undefined ? this.named.disableCheckScrollBounds : isTesting(); } /** @method mouseDown */ @action - mouseDown(event) { + mouseDown(event: MouseEvent) { if (event.which !== 1) { return; } @@ -300,7 +357,7 @@ export default class SortableItemModifier extends Modifier { } @action - keyDown(event) { + keyDown(event: Event) { if (this.isDisabled) { return; } @@ -319,15 +376,15 @@ export default class SortableItemModifier extends Modifier { @method touchStart */ @action - touchStart(event) { + touchStart(event: TouchEvent) { this._primeDrag(event); } /** @method freeze */ - freeze() { - let el = this.element; + freeze(): void { + const el = this.element; if (!el) { return; } @@ -338,8 +395,8 @@ export default class SortableItemModifier extends Modifier { /** @method reset */ - reset() { - let el = this.element; + reset(): void { + const el = this.element; if (!el) { return; } @@ -353,8 +410,8 @@ export default class SortableItemModifier extends Modifier { /** @method thaw */ - thaw() { - let el = this.element; + thaw(): void { + const el = this.element; if (!el) { return; } @@ -362,6 +419,9 @@ export default class SortableItemModifier extends Modifier { el.style.transition = ''; } + _prepareDragListener!: () => void; + _cancelStartDragListener!: () => void; + /** * Setup event listeners for drag and drop * @@ -369,12 +429,12 @@ export default class SortableItemModifier extends Modifier { * @param {Event} startEvent JS Event object * @private */ - _primeDrag(startEvent) { + _primeDrag(startEvent: Event) { if (this.isDisabled) { return; } - if (this.handleElement && !startEvent.target.closest(this.handle)) { + if (this.handleElement && !(startEvent.target as HTMLElement | null)?.closest(this.handle)) { return; } @@ -405,14 +465,14 @@ export default class SortableItemModifier extends Modifier { * @param {Event} event JS Event object * @private */ - _prepareDrag(startEvent, event) { + _prepareDrag(startEvent: MouseEvent, event: MouseEvent) { // Block drag start while any item has busy state if (this.sortableGroup.sortedItems.some((x) => x.isBusy)) { return; } - let distance = this.distance; - let dx = Math.abs(getX(startEvent) - getX(event)); - let dy = Math.abs(getY(startEvent) - getY(event)); + const distance = this.distance; + const dx = Math.abs(getX(startEvent) - getX(event)); + const dy = Math.abs(getY(startEvent) - getY(event)); if (distance <= dx || distance <= dy) { DRAG_ACTIONS.forEach((event) => window.removeEventListener(event, this._prepareDragListener)); @@ -427,15 +487,15 @@ export default class SortableItemModifier extends Modifier { * @param {Event} event JS Event object * @private */ - _startDrag(event) { + _startDrag(event: MouseEvent | TouchEvent) { if (this.isBusy) { return; } - let drag = this._makeDragHandler(event); - let dragThrottled = (ev) => throttle(this, drag, ev, 16, false); + const drag = this._makeDragHandler(event); + const dragThrottled = (ev: Event) => throttle(this, drag, ev, 16, false); - let drop = () => { + const drop = () => { DRAG_ACTIONS.forEach((event) => window.removeEventListener(event, dragThrottled)); END_ACTIONS.forEach((event) => window.removeEventListener(event, drop)); @@ -460,11 +520,11 @@ export default class SortableItemModifier extends Modifier { */ maxScrollSpeed = 20; - _scrollOnEdges(drag) { - let groupDirection = this.direction; - let element = this.element; - let scrollContainer = new ScrollContainer(scrollParent(element)); - let itemContainer = { + _scrollOnEdges(drag: (event: FakeEvent | Event) => void) { + const groupDirection = this.direction; + const element = this.element; + const scrollContainer = new ScrollContainer(scrollParent(element)); + const itemContainer: ItemContainer = { width: parseInt(getComputedStyle(element).width, 10), get height() { return parseInt(getComputedStyle(element).height, 10); @@ -483,7 +543,10 @@ export default class SortableItemModifier extends Modifier { }, }; - let leadingEdgeKey, trailingEdgeKey, scrollKey, pageKey; + let leadingEdgeKey: keyof ItemContainer, + trailingEdgeKey: keyof ItemContainer, + scrollKey: keyof ScrollContainer, + pageKey: keyof FakeEvent; if (groupDirection === 'grid' || groupDirection === 'x') { leadingEdgeKey = 'left'; trailingEdgeKey = 'right'; @@ -496,25 +559,25 @@ export default class SortableItemModifier extends Modifier { pageKey = 'pageY'; } - let createFakeEvent = () => { + const createFakeEvent = (): FakeEvent | void => { if (this._pageX == null && this._pageY == null) { return; } return { - pageX: this._pageX, - pageY: this._pageY, - clientX: this._pageX, - clientY: this._pageY, + pageX: this._pageX ?? 0, + pageY: this._pageY ?? 0, + clientX: this._pageX ?? 0, + clientY: this._pageY ?? 0, }; }; // Set a trigger padding that will start scrolling // the box when the item reaches within padding pixels // of the edge of the scroll container. - let checkScrollBounds = () => { - let leadingEdge = itemContainer[leadingEdgeKey]; - let trailingEdge = itemContainer[trailingEdgeKey]; - let scroll = scrollContainer[scrollKey](); + const checkScrollBounds = () => { + const leadingEdge = itemContainer[leadingEdgeKey]; + const trailingEdge = itemContainer[trailingEdgeKey]; + const scroll = scrollContainer[scrollKey](); let delta = 0; if (trailingEdge >= scrollContainer[trailingEdgeKey]) { @@ -524,12 +587,12 @@ export default class SortableItemModifier extends Modifier { } if (delta !== 0) { - let speed = this.maxScrollSpeed; + const speed = this.maxScrollSpeed; delta = Math.min(Math.max(delta, -1 * speed), speed); delta = scrollContainer[scrollKey](scroll + delta) - scroll; - let event = createFakeEvent(); + const event = createFakeEvent(); if (event) { if (scrollContainer.isWindow) { event[pageKey] += delta; @@ -553,12 +616,16 @@ export default class SortableItemModifier extends Modifier { @return {Function} @private */ - _makeDragHandler(startEvent) { + _makeDragHandler(startEvent: FakeEvent | Event): (event: FakeEvent | Event) => void { const groupDirection = this.direction; - let dragOrigin; - let elementOrigin; - let scrollOrigin; - let parentElement = this.element.parentNode; + let dragOrigin: number; + let elementOrigin: number; + let scrollOrigin: number; + const parentElement = this.element.parentNode as HTMLElement | null; + + if (!parentElement) { + return () => {}; + } if (groupDirection === 'grid') { this.startEvent = startEvent; @@ -574,14 +641,14 @@ export default class SortableItemModifier extends Modifier { return (event) => { this._pageX = getX(event); - let dx = this._pageX - dragOriginX; - let scrollX = parentElement.getBoundingClientRect().left; - let x = elementOriginX + dx + (scrollOriginX - scrollX); + const dx = this._pageX - dragOriginX; + const scrollX = parentElement.getBoundingClientRect().left; + const x = elementOriginX + dx + (scrollOriginX - scrollX); this._pageY = getY(event); - let dy = this._pageY - dragOriginY; - let scrollY = parentElement.getBoundingClientRect().top; - let y = elementOriginY + dy + (scrollOriginY - scrollY); + const dy = this._pageY - dragOriginY; + const scrollY = parentElement.getBoundingClientRect().top; + const y = elementOriginY + dy + (scrollOriginY - scrollY); this._drag(x, y); }; @@ -594,9 +661,9 @@ export default class SortableItemModifier extends Modifier { return (event) => { this._pageX = getX(event); - let dx = this._pageX - dragOrigin; - let scrollX = parentElement.getBoundingClientRect().left; - let x = elementOrigin + dx + (scrollOrigin - scrollX); + const dx = this._pageX - dragOrigin; + const scrollX = parentElement.getBoundingClientRect().left; + const x = elementOrigin + dx + (scrollOrigin - scrollX); this._drag(x, 0); }; @@ -609,28 +676,30 @@ export default class SortableItemModifier extends Modifier { return (event) => { this._pageY = getY(event); - let dy = this._pageY - dragOrigin; - let scrollY = parentElement.getBoundingClientRect().top; - let y = elementOrigin + dy + (scrollOrigin - scrollY); + const dy = this._pageY - dragOrigin; + const scrollY = parentElement.getBoundingClientRect().top; + const y = elementOrigin + dy + (scrollOrigin - scrollY); this._drag(0, y); }; } + + return () => {}; } /** @method _scheduleApplyPosition @private */ - _scheduleApplyPosition() { - scheduleOnce('render', this, '_applyPosition'); + _scheduleApplyPosition(): void { + scheduleOnce('render', this, this._applyPosition); } /** @method _applyPosition @private */ - _applyPosition() { + _applyPosition(): void { if (!this.element || !this.element) { return; } @@ -638,23 +707,23 @@ export default class SortableItemModifier extends Modifier { const groupDirection = this.direction; if (groupDirection === 'grid') { - let x = this.x; - let dx = x - this.element.offsetLeft + parseFloat(getComputedStyle(this.element).marginLeft); + const x = this.x; + const dx = x - this.element.offsetLeft + parseFloat(getComputedStyle(this.element).marginLeft); - let y = this.y; - let dy = y - this.element.offsetTop; + const y = this.y; + const dy = y - this.element.offsetTop; this.element.style.transform = `translate(${dx}px, ${dy}px)`; } if (groupDirection === 'x') { - let x = this.x; - let dx = x - this.element.offsetLeft + parseFloat(getComputedStyle(this.element).marginLeft); + const x = this.x; + const dx = x - this.element.offsetLeft + parseFloat(getComputedStyle(this.element).marginLeft); this.element.style.transform = `translateX(${dx}px)`; } if (groupDirection === 'y') { - let y = this.y; - let dy = y - this.element.offsetTop; + const y = this.y; + const dy = y - this.element.offsetTop; this.element.style.transform = `translateY(${dy}px)`; } @@ -664,15 +733,16 @@ export default class SortableItemModifier extends Modifier { @method _drag @private */ - _drag(dimensionX, dimensionY) { + _drag(dimensionX: number, dimensionY: number) { if (!this.isDragging) { return; } - let updateInterval = this.updateInterval; + const updateInterval = this.updateInterval; this.x = dimensionX; this.y = dimensionY; + // @ts-expect-error Argument of type 'this' is not assignable to parameter of type 'AnyFn'. throttle(this, this.sortableGroup.update, updateInterval); } @@ -680,12 +750,12 @@ export default class SortableItemModifier extends Modifier { @method _drop @private */ - _drop() { + _drop(): void { if (!this.element) { return; } - let transitionPromise = this._waitForTransition(); + const transitionPromise = this._waitForTransition(); this._preventClick(); @@ -699,7 +769,7 @@ export default class SortableItemModifier extends Modifier { this.sortableGroup.update(sortedItems); - let allTransitionPromise = this._waitForAllTransitions(); + const allTransitionPromise = this._waitForAllTransitions(); Promise.all([transitionPromise, allTransitionPromise]).then(() => this._complete()); } @@ -708,8 +778,8 @@ export default class SortableItemModifier extends Modifier { @method _preventClick @private */ - _preventClick() { - const selfCancellingCallback = (event) => { + _preventClick(): void { + const selfCancellingCallback = (event: Event) => { this.element.removeEventListener(ELEMENT_CLICK_ACTION, selfCancellingCallback); this._preventClickHandler(event); }; @@ -721,7 +791,7 @@ export default class SortableItemModifier extends Modifier { @method _preventClickHandler @private */ - _preventClickHandler(e) { + _preventClickHandler(e: Event): void { e.stopPropagation(); e.preventDefault(); e.stopImmediatePropagation(); @@ -733,7 +803,7 @@ export default class SortableItemModifier extends Modifier { @return Promise */ _waitForTransition() { - let waiterToken; + let waiterToken: unknown; if (DEBUG) { waiterToken = sortableItemWaiter.beginAsync(); @@ -767,7 +837,7 @@ export default class SortableItemModifier extends Modifier { @return Promise */ _waitForAllTransitions() { - let waiterToken; + let waiterToken: unknown; if (DEBUG) { waiterToken = sortableItemWaiter.beginAsync(); @@ -779,7 +849,7 @@ export default class SortableItemModifier extends Modifier { const animations = this.sortableGroup.sortedItems.map((x) => x.element.getAnimations()); const animationPromises = animations.map((animation) => { - return animation.finished; + return animation.every((x) => x.finished); }); transitionPromise = Promise.all(animationPromises); @@ -801,20 +871,20 @@ export default class SortableItemModifier extends Modifier { @method _complete @private */ - _complete() { + _complete(): void { this.onDragStop(this.model); set(this, 'isDropping', false); set(this, 'wasDropped', true); this.sortableGroup.commit(); } - get isAnimated() { + get isAnimated(): boolean | undefined { if (!this.element) { return undefined; } - let el = this.element; - let transitionProperty = getComputedStyle(el).transitionProperty; + const el = this.element; + const transitionProperty = getComputedStyle(el).transitionProperty; return /all|transform/.test(transitionProperty) && this.transitionDuration > 0; } @@ -824,15 +894,15 @@ export default class SortableItemModifier extends Modifier { @property transitionDuration @type Number */ - get transitionDuration() { + get transitionDuration(): number { const items = this.sortableGroup.sortedItems.filter((x) => !x.isDragging && !x.isDropping); - let el = items[0]?.element ?? this.element; // Fallback when only one element is present in list - let rule = getComputedStyle(el).transitionDuration; - let match = rule.match(/([\d.]+)([ms]*)/); + const el = items[0]?.element ?? this.element; // Fallback when only one element is present in list + const rule = getComputedStyle(el).transitionDuration; + const match = rule.match(/([\d.]+)([ms]*)/); if (match) { - let value = parseFloat(match[1]); - let unit = match[2]; + let value = parseFloat(match[1] ?? ''); + const unit = match[2]; if (unit === 's') { value = value * 1000; @@ -849,15 +919,15 @@ export default class SortableItemModifier extends Modifier { @property x @type Number */ - get x() { + get x(): number { if (this._x === undefined) { - let marginLeft = parseFloat(getComputedStyle(this.element).marginLeft); + const marginLeft = parseFloat(getComputedStyle(this.element).marginLeft); this._x = this.element.scrollLeft + this.element.offsetLeft - marginLeft; } return this._x; } - set x(value) { + set x(value: number) { if (value !== this._x) { this._x = value; this._scheduleApplyPosition(); @@ -869,7 +939,7 @@ export default class SortableItemModifier extends Modifier { @property y @type Number */ - get y() { + get y(): number { if (this._y === undefined) { this._y = this.element.offsetTop; } @@ -877,7 +947,7 @@ export default class SortableItemModifier extends Modifier { return this._y; } - set y(value) { + set y(value: number) { if (value !== this._y) { this._y = value; this._scheduleApplyPosition(); @@ -889,10 +959,10 @@ export default class SortableItemModifier extends Modifier { @property height @type Number */ - get width() { - let el = this.element; + get width(): number { + const el = this.element; let width = el.offsetWidth; - let elStyles = getComputedStyle(el); + const elStyles = getComputedStyle(el); width += parseInt(elStyles.marginLeft) + parseInt(elStyles.marginRight); // equal to jQuery.outerWidth(true) @@ -906,10 +976,10 @@ export default class SortableItemModifier extends Modifier { @property height @type Number */ - get height() { - let el = this.element; + get height(): number { + const el = this.element; let height = el.offsetHeight; - let elStyles = getComputedStyle(el); + const elStyles = getComputedStyle(el); // This is needed atm only for grid, to fix jumping on drag-start. // In test-app it looks like there is a side-effect when we activate also for direction vertical. @@ -944,26 +1014,31 @@ export default class SortableItemModifier extends Modifier { const touchAction = disabled ? 'initial' : 'none'; if (this.handleElement) { - this.handleElement.style['touch-action'] = touchAction; + this.handleElement.style['touchAction'] = touchAction; } else { - this.element.style['touch-action'] = touchAction; + this.element.style['touchAction'] = touchAction; } } - element; + element!: HTMLElement; didSetup = false; + named!: NamedArgs>; /** * tracks if event listeners have been registered. Registering event handlers is unnecessary if item is disabled. */ listenersRegistered = false; - constructor(owner, args) { + constructor(owner: Owner, args: ArgsFor>) { super(owner, args); registerDestructor(this, cleanup); } - modify(element, _positional, named) { + override modify( + element: HTMLElement, + _positional: PositionalArgs>, + named: NamedArgs>, + ) { this.element = element; this.named = named; @@ -974,7 +1049,7 @@ export default class SortableItemModifier extends Modifier { this.setupHandleElement(this.named.disabled); if (!this.didSetup) { - this.element.dataset.sortableItem = true; + this.element.dataset['sortableItem'] = 'true'; this.sortableService.registerItem(this.groupName, this); this.didSetup = true; } @@ -991,7 +1066,7 @@ export default class SortableItemModifier extends Modifier { * * @param {SortableItemModifier} instance */ -function cleanup(instance) { +function cleanup(instance: SortableItemModifier) { instance.removeEventListener(); instance.sortableService.deregisterItem(instance.groupName, instance); } diff --git a/addon/src/services/ember-sortable-internal-state.js b/addon/src/services/ember-sortable-internal-state.ts similarity index 58% rename from addon/src/services/ember-sortable-internal-state.js rename to addon/src/services/ember-sortable-internal-state.ts index be6e9c10..4a18abf1 100644 --- a/addon/src/services/ember-sortable-internal-state.js +++ b/addon/src/services/ember-sortable-internal-state.ts @@ -1,6 +1,13 @@ import Service from '@ember/service'; +import type SortableGroupModifier from '../modifiers/sortable-group'; +import type SortableItemModifier from '../modifiers/sortable-item'; -export default class EmberSortableService extends Service { +export interface Group { + groupModifier: SortableGroupModifier | undefined; + items: SortableItemModifier[]; +} + +export default class EmberSortableService extends Service { /** * Internal State for any groups currently in DOM * @@ -12,7 +19,7 @@ export default class EmberSortableService extends Service { * } * @type {{}} */ - groups = {}; + groups: Record> = {}; /** * Register a new group with the service @@ -20,7 +27,7 @@ export default class EmberSortableService extends Service { * @param {String} groupName * @param {SortableGroupModifier} groupModifier */ - registerGroup(groupName, groupModifier) { + registerGroup(groupName: string, groupModifier: SortableGroupModifier | undefined) { if (this.groups[groupName] === undefined) { this.groups[groupName] = { groupModifier: groupModifier, @@ -36,7 +43,7 @@ export default class EmberSortableService extends Service { * * @param {String} groupName */ - deregisterGroup(groupName) { + deregisterGroup(groupName: string): void { delete this.groups[groupName]; } @@ -47,8 +54,13 @@ export default class EmberSortableService extends Service { * @param {String} groupName * @param {SortableItemModifier} item */ - registerItem(groupName, item) { - let groupDef = this.fetchGroup(groupName); + registerItem(groupName: string, item: SortableItemModifier): void { + const groupDef = this.fetchGroup(groupName); + + if (!groupDef) { + return; + } + let items = groupDef.items; if (items.indexOf(item) === -1) { @@ -65,13 +77,18 @@ export default class EmberSortableService extends Service { @param groupName @param item */ - deregisterItem(groupName, item) { - let groupDef = this.fetchGroup(groupName); - let items = groupDef.items; + deregisterItem(groupName: string, item: SortableItemModifier): void { + const groupDef = this.fetchGroup(groupName); + + if (!groupDef) { + return; + } + + const items = groupDef.items; const index = items.indexOf(item); if (index !== -1) { - let newItems = [...items.slice(0, index), ...items.slice(index + 1)]; + const newItems = [...items.slice(0, index), ...items.slice(index + 1)]; groupDef.items = newItems; } } @@ -82,11 +99,11 @@ export default class EmberSortableService extends Service { * @param {String} groupName * @returns {*} */ - fetchGroup(groupName) { + fetchGroup(groupName: string): Group { if (this.groups[groupName] === undefined) { this.registerGroup(groupName, undefined); } - return this.groups[groupName]; + return this.groups[groupName]!; } } diff --git a/addon/src/system/scroll-container.js b/addon/src/system/scroll-container.ts similarity index 74% rename from addon/src/system/scroll-container.js rename to addon/src/system/scroll-container.ts index 92d70b93..71c2a28a 100644 --- a/addon/src/system/scroll-container.js +++ b/addon/src/system/scroll-container.ts @@ -1,7 +1,18 @@ export default class ScrollContainer { - constructor(element) { + element: HTMLElement; + isWindow: boolean; + top: number; + left: number; + width: number; + height: number; + scrollWidth: number; + scrollHeight: number; + maxScrollTop: number; + maxScrollLeft: number; + + constructor(element: HTMLElement | Document) { this.isWindow = element === document; - this.element = this.isWindow ? document.documentElement : element; + this.element = this.isWindow ? document.documentElement : (element as HTMLElement); if (this.isWindow) { this.top = 0; @@ -10,7 +21,7 @@ export default class ScrollContainer { this.height = document.documentElement.clientHeight; } else { // Absolute position in document - let { top, left } = this.element.getBoundingClientRect(); + const { top, left } = this.element.getBoundingClientRect(); this.top = top; this.left = left; // Viewport size of the container element @@ -33,7 +44,7 @@ export default class ScrollContainer { return this.left + this.width; } - scrollTop(value) { + scrollTop(value?: number) { if (value) { value = Math.max(0, Math.min(this.maxScrollTop, value)); this.element.scrollTop = value; @@ -42,7 +53,7 @@ export default class ScrollContainer { return this.element.scrollTop; } - scrollLeft(value) { + scrollLeft(value?: number) { if (value) { value = Math.max(0, Math.min(this.maxScrollLeft, value)); this.element.scrollLeft = value; diff --git a/addon/src/system/scroll-parent.js b/addon/src/system/scroll-parent.js deleted file mode 100644 index 5e108ed2..00000000 --- a/addon/src/system/scroll-parent.js +++ /dev/null @@ -1,33 +0,0 @@ -function getParentElements(element) { - const parentsArray = []; - - if (!element) { - return parentsArray; - } - - let currentParent = element.parentElement; - - while (currentParent !== null) { - parentsArray.push(currentParent); - currentParent = currentParent.parentElement; - } - return parentsArray; -} - -export default function (element) { - let position = getComputedStyle(element).position; - let excludeStaticParent = position === 'absolute'; - let scrollParent = getParentElements(element).filter(function (parent) { - let parentElemStyles = getComputedStyle(parent); - if (excludeStaticParent && parentElemStyles.position === 'static') { - return false; - } - let { overflow, overflowX, overflowY } = parentElemStyles; - return /(auto|scroll)/.test(overflow + overflowX + overflowY); - })[0]; - - if (!scrollParent || scrollParent === document.body) { - scrollParent = document; - } - return position === 'fixed' || scrollParent; -} diff --git a/addon/src/system/scroll-parent.ts b/addon/src/system/scroll-parent.ts new file mode 100644 index 00000000..7f2b906b --- /dev/null +++ b/addon/src/system/scroll-parent.ts @@ -0,0 +1,33 @@ +function getParentElements(element: HTMLElement): HTMLElement[] { + const parentsArray: HTMLElement[] = []; + + if (!element) { + return parentsArray; + } + + let currentParent = element.parentElement; + + while (currentParent !== null) { + parentsArray.push(currentParent); + currentParent = currentParent.parentElement; + } + return parentsArray; +} + +export default function (element: HTMLElement): HTMLElement | Document { + const position = getComputedStyle(element).position; + const excludeStaticParent = position === 'absolute'; + let scrollParent: HTMLElement | Document | undefined = getParentElements(element).filter(function (parent) { + const parentElemStyles = getComputedStyle(parent); + if (excludeStaticParent && parentElemStyles.position === 'static') { + return false; + } + const { overflow, overflowX, overflowY } = parentElemStyles; + return /(auto|scroll)/.test(overflow + overflowX + overflowY); + })[0]; + + if (!scrollParent || scrollParent === document.body) { + scrollParent = document; + } + return scrollParent; +} diff --git a/addon/src/template-registry.ts b/addon/src/template-registry.ts new file mode 100644 index 00000000..1d8f5928 --- /dev/null +++ b/addon/src/template-registry.ts @@ -0,0 +1,13 @@ +// Easily allow apps, which are not yet using strict mode templates, to consume your Glint types, by importing this file. +// Add all your components, helpers and modifiers to the template registry here, so apps don't have to do this. +// See https://typed-ember.gitbook.io/glint/environments/ember/authoring-addons + +import type SortableGroupModifier from './modifiers/sortable-group'; +import type SortableHandleModifier from './modifiers/sortable-handle'; +import type SortableItemModifier from './modifiers/sortable-item'; + +export default interface EmberSortableRegistry { + 'sortable-group': typeof SortableGroupModifier; + 'sortable-handle': typeof SortableHandleModifier; + 'sortable-item': typeof SortableItemModifier; +} diff --git a/addon/src/test-support/helpers/drag.js b/addon/src/test-support/helpers/drag.ts similarity index 79% rename from addon/src/test-support/helpers/drag.js rename to addon/src/test-support/helpers/drag.ts index 28cb379a..f8e2e668 100644 --- a/addon/src/test-support/helpers/drag.js +++ b/addon/src/test-support/helpers/drag.ts @@ -1,5 +1,5 @@ import { triggerEvent, find, settled, waitUntil } from '@ember/test-helpers'; -import { getOffset } from '../utils/offset'; +import { getOffset } from '../utils/offset.ts'; /** Drags elements by an offset specified in pixels. @@ -26,7 +26,21 @@ import { getOffset } from '../utils/offset'; @return {Promise} */ -export async function drag(mode, itemSelector, offsetFn, callbacks = {}) { +export type TMode = 'mouse' | 'touch'; + +interface Callbacks { + dragstart?: () => Promise; + dragmove?: () => Promise; + beforedragend?: () => Promise; + dragend?: () => Promise; +} + +export async function drag( + mode: TMode, + itemSelector: string, + offsetFn: () => { dx: number; dy: number }, + callbacks: Callbacks = {}, +) { let start; let move; let end; @@ -46,6 +60,11 @@ export async function drag(mode, itemSelector, offsetFn, callbacks = {}) { } const itemElement = find(itemSelector); + + if (!itemElement) { + throw new Error(`Element with selector '${itemSelector}' not found!`); + } + const itemOffset = getOffset(itemElement); const offset = offsetFn(); const rect = itemElement.getBoundingClientRect(); @@ -57,7 +76,11 @@ export async function drag(mode, itemSelector, offsetFn, callbacks = {}) { // https://stackoverflow.com/a/5042051 const dx = offset.dx || 0; const dy = offset.dy || 0; - const clientHeight = itemElement.clientHeight || itemElement.offsetHeight || itemElement.parentNode.offsetHeight; + const clientHeight = + itemElement.clientHeight || + (itemElement as HTMLElement).offsetHeight || + (itemElement.parentNode as HTMLElement | null)?.offsetHeight || + 0; const scale = clientHeight / (rect.bottom - rect.top); const halfwayX = itemOffset.left + (dx * scale) / 2; const halfwayY = itemOffset.top + (dy * scale) / 2; diff --git a/addon/src/test-support/helpers/reorder.js b/addon/src/test-support/helpers/reorder.ts similarity index 56% rename from addon/src/test-support/helpers/reorder.js rename to addon/src/test-support/helpers/reorder.ts index 3a302286..776b8ee1 100644 --- a/addon/src/test-support/helpers/reorder.js +++ b/addon/src/test-support/helpers/reorder.ts @@ -1,6 +1,6 @@ import { find, findAll } from '@ember/test-helpers'; -import { drag } from './drag'; -import { getOffset } from '../utils/offset'; +import { drag, type TMode } from './drag.ts'; +import { getOffset } from '../utils/offset.ts'; const OVERSHOOT = 2; @@ -26,15 +26,34 @@ const OVERSHOOT = 2; selectors for the resultant order @return {Promise} */ -export async function reorder(mode, itemSelector, ...resultSelectors) { +export async function reorder( + mode: TMode, + itemSelector: string, + ...resultSelectors: T[] +) { for (let targetIndex = 0; targetIndex < resultSelectors.length; targetIndex++) { const items = findAll(itemSelector); - const sourceElement = find(resultSelectors[targetIndex]); + const result = resultSelectors[targetIndex]; + if (!result) { + throw new Error(`Element on position ${targetIndex} not found!`); + } + + const sourceElement = find(result); + + if (!sourceElement) { + throw new Error(`SourceElement with selector '${result}' not found!`); + } + const targetElement = items[targetIndex]; + + if (!targetElement) { + throw new Error(`TargetElement not found! Selector '${result}' has no element on index ${targetIndex}!`); + } + const dx = getOffset(targetElement).left - OVERSHOOT - getOffset(sourceElement).left; const dy = getOffset(targetElement).top - OVERSHOOT - getOffset(sourceElement).top; - await drag(mode, sourceElement, () => { + await drag(mode, result, () => { return { dx: dx, dy: dy }; }); } diff --git a/addon/src/test-support/index.js b/addon/src/test-support/index.js deleted file mode 100644 index 3f88e12f..00000000 --- a/addon/src/test-support/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export * from './utils/keyboard'; -export { getOffset } from './utils/offset'; -export { drag } from './helpers/drag'; -export { reorder } from './helpers/reorder'; diff --git a/addon/src/test-support/index.ts b/addon/src/test-support/index.ts new file mode 100644 index 00000000..45bed54b --- /dev/null +++ b/addon/src/test-support/index.ts @@ -0,0 +1,4 @@ +export * from './utils/keyboard.ts'; +export { getOffset } from './utils/offset.ts'; +export { drag } from './helpers/drag.ts'; +export { reorder } from './helpers/reorder.ts'; diff --git a/addon/src/test-support/utils/keyboard.js b/addon/src/test-support/utils/keyboard.ts similarity index 90% rename from addon/src/test-support/utils/keyboard.js rename to addon/src/test-support/utils/keyboard.ts index 73fc9c1c..c2ac899a 100644 --- a/addon/src/test-support/utils/keyboard.js +++ b/addon/src/test-support/utils/keyboard.ts @@ -17,8 +17,8 @@ export const ARROW_KEY_CODES = { DOWN: 40, }; -function createKeyTest(key, keyCode) { - return function isKey(event) { +function createKeyTest(key: string, keyCode: number) { + return function isKey(event: KeyboardEvent) { return event.key === key || event.keyCode === keyCode; }; } diff --git a/addon/src/test-support/utils/offset.js b/addon/src/test-support/utils/offset.ts similarity index 90% rename from addon/src/test-support/utils/offset.js rename to addon/src/test-support/utils/offset.ts index 8b05366d..5df9b792 100644 --- a/addon/src/test-support/utils/offset.js +++ b/addon/src/test-support/utils/offset.ts @@ -4,7 +4,7 @@ * @method getOffset * @param {Element} el an element */ -export function getOffset(el) { +export function getOffset(el: Element) { const box = el.getBoundingClientRect(); return { diff --git a/addon/src/utils/constant.js b/addon/src/utils/constant.js deleted file mode 100644 index 6c6400e1..00000000 --- a/addon/src/utils/constant.js +++ /dev/null @@ -1,9 +0,0 @@ -export const DRAG_ACTIONS = ['mousemove', 'touchmove']; -export const ELEMENT_CLICK_ACTION = 'click'; -export const END_ACTIONS = ['click', 'mouseup', 'touchend']; -export const ANNOUNCEMENT_ACTION_TYPES = { - ACTIVATE: 'ACTIVATE', - MOVE: 'MOVE', - CONFIRM: 'CONFIRM', - CANCEL: 'CANCEL', -}; diff --git a/addon/src/utils/constant.ts b/addon/src/utils/constant.ts new file mode 100644 index 00000000..68b2ba1c --- /dev/null +++ b/addon/src/utils/constant.ts @@ -0,0 +1,16 @@ +import type { A11yAnnouncementConfig } from './defaults'; + +export const DRAG_ACTIONS: (keyof WindowEventMap)[] = ['mousemove', 'touchmove']; +export const ELEMENT_CLICK_ACTION = 'click'; +export const END_ACTIONS = ['click', 'mouseup', 'touchend']; +export const ANNOUNCEMENT_ACTION_TYPES: { + ACTIVATE: keyof A11yAnnouncementConfig; + MOVE: keyof A11yAnnouncementConfig; + CONFIRM: keyof A11yAnnouncementConfig; + CANCEL: keyof A11yAnnouncementConfig; +} = { + ACTIVATE: 'ACTIVATE', + MOVE: 'MOVE', + CONFIRM: 'CONFIRM', + CANCEL: 'CANCEL', +}; diff --git a/addon/src/utils/coordinate.js b/addon/src/utils/coordinate.js deleted file mode 100644 index 78a106c5..00000000 --- a/addon/src/utils/coordinate.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - Gets the y offset for a given event. - Work for touch and mouse events. - @method getY - @return {Number} - @private -*/ -export function getY(event) { - let touches = event.changedTouches; - let touch = touches && touches[0]; - - if (touch) { - return touch.screenY; - } else { - return event.clientY; - } -} - -/** - Gets the x offset for a given event. - @method getX - @return {Number} - @private -*/ -export function getX(event) { - let touches = event.changedTouches; - let touch = touches && touches[0]; - - if (touch) { - return touch.screenX; - } else { - return event.clientX; - } -} diff --git a/addon/src/utils/coordinate.ts b/addon/src/utils/coordinate.ts new file mode 100644 index 00000000..7c1fee00 --- /dev/null +++ b/addon/src/utils/coordinate.ts @@ -0,0 +1,36 @@ +import type { FakeEvent } from '../modifiers/sortable-item'; + +/** + Gets the y offset for a given event. + Work for touch and mouse events. + @method getY + @return {Number} + @private +*/ +export function getY(event: FakeEvent | Event): number { + const touches = (event as TouchEvent).changedTouches; + const touch = touches && touches[0]; + + if (touch) { + return touch.screenY; + } else { + return (event as MouseEvent).clientY; + } +} + +/** + Gets the x offset for a given event. + @method getX + @return {Number} + @private +*/ +export function getX(event: FakeEvent | Event): number { + const touches = (event as TouchEvent).changedTouches; + const touch = touches && touches[0]; + + if (touch) { + return touch.screenX; + } else { + return (event as MouseEvent).clientX; + } +} diff --git a/addon/src/utils/css-calculation.js b/addon/src/utils/css-calculation.js deleted file mode 100644 index 98eb1ee5..00000000 --- a/addon/src/utils/css-calculation.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - Gets a numeric border-spacing values for a given element. - - @method getBorderSpacing - @param {Element} element - @return {Object} - @private -*/ -export function getBorderSpacing(el) { - let css = getComputedStyle(el).borderSpacing; // '0px 0px' - let [horizontal, vertical] = css.split(' '); - - return { - horizontal: parseFloat(horizontal), - vertical: parseFloat(vertical), - }; -} diff --git a/addon/src/utils/css-calculation.ts b/addon/src/utils/css-calculation.ts new file mode 100644 index 00000000..eab8e19b --- /dev/null +++ b/addon/src/utils/css-calculation.ts @@ -0,0 +1,17 @@ +/** + Gets a numeric border-spacing values for a given element. + + @method getBorderSpacing + @param {Element} element + @return {Object} + @private +*/ +export function getBorderSpacing(el: Element) { + const css = getComputedStyle(el).borderSpacing; // '0px 0px' + const [horizontal, vertical] = css.split(' '); + + return { + horizontal: parseFloat(horizontal ?? ''), + vertical: parseFloat(vertical ?? ''), + }; +} diff --git a/addon/src/utils/defaults.js b/addon/src/utils/defaults.ts similarity index 55% rename from addon/src/utils/defaults.js rename to addon/src/utils/defaults.ts index a25d7604..fc885e2c 100644 --- a/addon/src/utils/defaults.js +++ b/addon/src/utils/defaults.ts @@ -1,4 +1,33 @@ -export const defaultA11yAnnouncementConfig = { +import type { TDirection } from '../modifiers/sortable-group.ts'; + +export interface A11yAnnouncementConfig { + ACTIVATE: ({ + a11yItemName, + index, + maxLength, + direction, + }: { + a11yItemName: string; + index: number; + maxLength: number; + direction: TDirection; + }) => string; + MOVE: ({ + a11yItemName, + index, + maxLength, + delta, + }: { + a11yItemName: string; + index: number; + maxLength: number; + delta: number; + }) => string; + CONFIRM: ({ a11yItemName }: { a11yItemName: string }) => string; + CANCEL: ({ a11yItemName }: { a11yItemName: string }) => string; +} + +export const defaultA11yAnnouncementConfig: A11yAnnouncementConfig = { ACTIVATE({ a11yItemName, index, maxLength, direction }) { let message = `${a11yItemName} at position, ${index + 1} of ${maxLength}, is activated to be repositioned.`; diff --git a/addon/src/utils/keyboard.js b/addon/src/utils/keyboard.ts similarity index 86% rename from addon/src/utils/keyboard.js rename to addon/src/utils/keyboard.ts index 73fc9c1c..830ad5e9 100644 --- a/addon/src/utils/keyboard.js +++ b/addon/src/utils/keyboard.ts @@ -17,8 +17,8 @@ export const ARROW_KEY_CODES = { DOWN: 40, }; -function createKeyTest(key, keyCode) { - return function isKey(event) { +function createKeyTest(key: string, keyCode: number): (event: KeyboardEvent) => boolean { + return function isKey(event: KeyboardEvent): boolean { return event.key === key || event.keyCode === keyCode; }; } diff --git a/addon/tsconfig.json b/addon/tsconfig.json new file mode 100644 index 00000000..d13a95f3 --- /dev/null +++ b/addon/tsconfig.json @@ -0,0 +1,65 @@ +{ + "extends": "@tsconfig/ember/tsconfig.json", + "include": [ + "src/**/*", + "unpublished-development-types/**/*" + ], + "glint": { + "environment": ["ember-loose", "ember-template-imports"] + }, + "compilerOptions": { + "allowJs": true, + "declarationDir": "declarations", + "allowSyntheticDefaultImports": true, + "noImplicitAny": true, + "noImplicitThis": true, + "alwaysStrict": true, + "exactOptionalPropertyTypes": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + /** + https://www.typescriptlang.org/tsconfig#noEmit + + We want to emit declarations, so this option must be set to `false`. + @tsconfig/ember sets this to `true`, which is incompatible with our need to set `emitDeclarationOnly`. + @tsconfig/ember is more optimized for apps, which wouldn't emit anything, only type check. + */ + "noEmit": false, + /** + https://www.typescriptlang.org/tsconfig#emitDeclarationOnly + We want to only emit declarations as we use Rollup to emit JavaScript. + */ + "emitDeclarationOnly": true, + + /** + https://www.typescriptlang.org/tsconfig#noEmitOnError + Do not block emit on TS errors. + */ + "noEmitOnError": false, + + /** + https://www.typescriptlang.org/tsconfig#rootDir + "Default: The longest common path of all non-declaration input files." + + Because we want our declarations' structure to match our rollup output, + we need this "rootDir" to match the "srcDir" in the rollup.config.mjs. + + This way, we can have simpler `package.json#exports` that matches + imports to files on disk + */ + "rootDir": "./src", + + /** + https://www.typescriptlang.org/tsconfig#allowImportingTsExtensions + + We want our tooling to know how to resolve our custom files so the appropriate plugins + can do the proper transformations on those files. + */ + "allowImportingTsExtensions": true + } +} diff --git a/addon/unpublished-development-types/index.d.ts b/addon/unpublished-development-types/index.d.ts new file mode 100644 index 00000000..b0440add --- /dev/null +++ b/addon/unpublished-development-types/index.d.ts @@ -0,0 +1,14 @@ +// Add any types here that you need for local development only. +// These will *not* be published as part of your addon, so be careful that your published code does not rely on them! + +import '@glint/environment-ember-loose'; +import '@glint/environment-ember-template-imports'; + +declare module '@glint/environment-ember-loose/registry' { + // Remove this once entries have been added! 👇 + // eslint-disable-next-line @typescript-eslint/no-empty-object-type + export default interface Registry { + // Add any registry entries from other addons here that your addon itself uses (in non-strict mode templates) + // See https://typed-ember.gitbook.io/glint/using-glint/ember/using-addons + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 083a2c37..7c6007fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,12 +17,21 @@ importers: '@embroider/addon-shim': specifier: ^1.8.9 version: 1.8.9 + '@embroider/macros': + specifier: ^1.16.6 + version: 1.16.6(@glint/template@1.4.0) + '@glimmer/env': + specifier: ^0.1.7 + version: 0.1.7 + rsvp: + specifier: ^4.8.5 + version: 4.8.5 devDependencies: '@babel/core': specifier: ^7.25.2 version: 7.25.2 '@babel/eslint-parser': - specifier: ^7.25.1 + specifier: ^7.11.0 version: 7.25.1(@babel/core@7.25.2)(eslint@8.57.1) '@babel/plugin-proposal-class-properties': specifier: ^7.18.6 @@ -30,24 +39,123 @@ importers: '@babel/plugin-proposal-decorators': specifier: ^7.24.7 version: 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-typescript': + specifier: ^7.25.2 + version: 7.25.2(@babel/core@7.25.2) '@ember/test-helpers': specifier: ^4.0.4 - version: 4.0.4(@babel/core@7.25.2)(ember-source@5.11.0) + version: 4.0.4(@babel/core@7.25.2)(@glint/template@1.4.0) '@ember/test-waiters': specifier: ^3.1.0 version: 3.1.0 '@embroider/addon-dev': specifier: ^5.0.0 - version: 5.0.0(rollup@3.29.5) + version: 5.0.0(@glint/template@1.4.0)(rollup@3.29.5) '@glimmer/component': specifier: ^1.1.2 version: 1.1.2(@babel/core@7.25.2) + '@glint/core': + specifier: ^1.4.0 + version: 1.4.0(typescript@5.6.2) + '@glint/environment-ember-loose': + specifier: ^1.4.0 + version: 1.4.0(@glimmer/component@1.1.2)(@glint/template@1.4.0)(@types/ember__array@4.0.10)(@types/ember__component@4.0.22)(@types/ember__controller@4.0.12)(@types/ember__object@4.0.12)(@types/ember__routing@4.0.22)(ember-modifier@4.2.0) + '@glint/environment-ember-template-imports': + specifier: ^1.4.0 + version: 1.4.0(@glint/environment-ember-loose@1.4.0)(@glint/template@1.4.0)(@types/ember__component@4.0.22)(@types/ember__helper@4.0.8)(@types/ember__modifier@4.0.9)(@types/ember__routing@4.0.22) + '@glint/template': + specifier: ^1.4.0 + version: 1.4.0 '@rollup/plugin-babel': specifier: ^6.0.4 version: 6.0.4(@babel/core@7.25.2)(rollup@3.29.5) + '@tsconfig/ember': + specifier: ^3.0.8 + version: 3.0.8 + '@types/ember': + specifier: ^4.0.11 + version: 4.0.11(@babel/core@7.25.2) + '@types/ember__application': + specifier: ^4.0.11 + version: 4.0.11(@babel/core@7.25.2) + '@types/ember__array': + specifier: ^4.0.10 + version: 4.0.10(@babel/core@7.25.2) + '@types/ember__component': + specifier: ^4.0.22 + version: 4.0.22(@babel/core@7.25.2) + '@types/ember__controller': + specifier: ^4.0.12 + version: 4.0.12(@babel/core@7.25.2) + '@types/ember__debug': + specifier: ^4.0.8 + version: 4.0.8(@babel/core@7.25.2) + '@types/ember__destroyable': + specifier: ^4.0.5 + version: 4.0.5 + '@types/ember__engine': + specifier: ^4.0.11 + version: 4.0.11(@babel/core@7.25.2) + '@types/ember__error': + specifier: ^4.0.6 + version: 4.0.6 + '@types/ember__helper': + specifier: ^4.0.8 + version: 4.0.8(@babel/core@7.25.2) + '@types/ember__modifier': + specifier: ^4.0.9 + version: 4.0.9(@babel/core@7.25.2) + '@types/ember__object': + specifier: ^4.0.12 + version: 4.0.12(@babel/core@7.25.2) + '@types/ember__owner': + specifier: ^4.0.9 + version: 4.0.9 + '@types/ember__polyfills': + specifier: ^4.0.6 + version: 4.0.6 + '@types/ember__routing': + specifier: ^4.0.22 + version: 4.0.22(@babel/core@7.25.2) + '@types/ember__runloop': + specifier: ^4.0.10 + version: 4.0.10(@babel/core@7.25.2) + '@types/ember__service': + specifier: ^4.0.9 + version: 4.0.9(@babel/core@7.25.2) + '@types/ember__string': + specifier: ^3.16.3 + version: 3.16.3 + '@types/ember__template': + specifier: ^4.0.7 + version: 4.0.7 + '@types/ember__test': + specifier: ^4.0.6 + version: 4.0.6(@babel/core@7.25.2) + '@types/ember__utils': + specifier: ^4.0.7 + version: 4.0.7(@babel/core@7.25.2) + '@types/rsvp': + specifier: ^4.0.9 + version: 4.0.9 + '@typescript-eslint/eslint-plugin': + specifier: ^8.3.0 + version: 8.6.0(@typescript-eslint/parser@8.6.0)(eslint@8.57.1)(typescript@5.6.2) + '@typescript-eslint/parser': + specifier: ^8.3.0 + version: 8.6.0(eslint@8.57.1)(typescript@5.6.2) + babel-plugin-ember-template-compilation: + specifier: ^2.2.5 + version: 2.3.0 + concurrently: + specifier: ^9.0.1 + version: 9.0.1 ember-modifier: specifier: ^4.2.0 version: 4.2.0(@babel/core@7.25.2)(ember-source@5.11.0) + ember-template-lint: + specifier: ^6.0.0 + version: 6.0.0 eslint: specifier: ^8.57.0 version: 8.57.1 @@ -56,7 +164,10 @@ importers: version: 9.1.0(eslint@8.57.1) eslint-plugin-ember: specifier: ^12.2.0 - version: 12.2.0(@babel/core@7.25.2)(eslint@8.57.1) + version: 12.2.0(@babel/core@7.25.2)(@typescript-eslint/parser@8.6.0)(eslint@8.57.1) + eslint-plugin-import: + specifier: ^2.30.0 + version: 2.30.0(@typescript-eslint/parser@8.6.0)(eslint@8.57.1) eslint-plugin-n: specifier: ^17.10.3 version: 17.10.3(eslint@8.57.1) @@ -66,9 +177,6 @@ importers: eslint-plugin-qunit: specifier: ^8.1.2 version: 8.1.2(eslint@8.57.1) - npm-run-all2: - specifier: ^6.2.3 - version: 6.2.3 prettier: specifier: ^3.3.3 version: 3.3.3 @@ -114,7 +222,7 @@ importers: version: 3.1.0 '@embroider/macros': specifier: ^1.16.6 - version: 1.16.6 + version: 1.16.6(@glint/template@1.4.0) '@embroider/test-setup': specifier: ^4.0.0 version: 4.0.0 @@ -192,7 +300,7 @@ importers: version: 9.1.0(eslint@8.57.1) eslint-plugin-ember: specifier: ^12.2.0 - version: 12.2.0(@babel/core@7.25.2)(eslint@8.57.1) + version: 12.2.0(@babel/core@7.25.2)(@typescript-eslint/parser@8.6.0)(eslint@8.57.1) eslint-plugin-n: specifier: ^17.10.3 version: 17.10.3(eslint@8.57.1) @@ -262,7 +370,7 @@ importers: version: 3.1.0 '@embroider/macros': specifier: ^1.16.6 - version: 1.16.6 + version: 1.16.6(@glint/template@1.4.0) '@embroider/test-setup': specifier: ^4.0.0 version: 4.0.0 @@ -299,9 +407,6 @@ importers: ember-cli-inject-live-reload: specifier: ^2.1.0 version: 2.1.0 - ember-disable-prototype-extensions: - specifier: ^1.1.3 - version: 1.1.3 ember-load-initializers: specifier: ^2.1.2 version: 2.1.2(@babel/core@7.25.2) @@ -337,7 +442,7 @@ importers: version: 9.1.0(eslint@8.57.1) eslint-plugin-ember: specifier: ^12.2.0 - version: 12.2.0(@babel/core@7.25.2)(eslint@8.57.1) + version: 12.2.0(@babel/core@7.25.2)(@typescript-eslint/parser@8.6.0)(eslint@8.57.1) eslint-plugin-n: specifier: ^17.10.3 version: 17.10.3(eslint@8.57.1) @@ -1757,6 +1862,23 @@ packages: resolution: {integrity: sha512-IMVyVE72twuAMSYcHzWSgtgYTtzlHlKSGW8vEbztnnmkU6uo7kVHmiqSN9R4RkBhzvh0VD4G76Eph+55t3iNIA==} dev: true + /@ember/test-helpers@4.0.4(@babel/core@7.25.2)(@glint/template@1.4.0): + resolution: {integrity: sha512-1mbOVyVEcLxYOGzBaeeaQkCrL1o9Av86QaHk/1RvrVBW24I6YUj1ILLEi2qLZT5PzcCy0TdfadHT3hKJwJ0GcQ==} + peerDependencies: + ember-source: '>= 4.0.0' + dependencies: + '@ember/test-waiters': 3.1.0 + '@embroider/addon-shim': 1.8.9 + '@embroider/macros': 1.16.6(@glint/template@1.4.0) + '@simple-dom/interface': 1.4.0 + decorator-transforms: 2.2.0(@babel/core@7.25.2) + dom-element-descriptors: 0.5.1 + transitivePeerDependencies: + - '@babel/core' + - '@glint/template' + - supports-color + dev: true + /@ember/test-helpers@4.0.4(@babel/core@7.25.2)(ember-source@5.11.0): resolution: {integrity: sha512-1mbOVyVEcLxYOGzBaeeaQkCrL1o9Av86QaHk/1RvrVBW24I6YUj1ILLEi2qLZT5PzcCy0TdfadHT3hKJwJ0GcQ==} peerDependencies: @@ -1764,7 +1886,7 @@ packages: dependencies: '@ember/test-waiters': 3.1.0 '@embroider/addon-shim': 1.8.9 - '@embroider/macros': 1.16.6 + '@embroider/macros': 1.16.6(@glint/template@1.4.0) '@simple-dom/interface': 1.4.0 decorator-transforms: 2.2.0(@babel/core@7.25.2) dom-element-descriptors: 0.5.1 @@ -1785,7 +1907,7 @@ packages: transitivePeerDependencies: - supports-color - /@embroider/addon-dev@5.0.0(rollup@3.29.5): + /@embroider/addon-dev@5.0.0(@glint/template@1.4.0)(rollup@3.29.5): resolution: {integrity: sha512-cEaPnhNJBqb+1Mp/1iDsfZjXU/odQ+vYqtqg19qh/28KBbbL5do1TzFPfXuGjzuDHiTIagklpGoNGkMwU/bJtw==} engines: {node: 12.* || 14.* || >= 16} hasBin: true @@ -1795,7 +1917,7 @@ packages: rollup: optional: true dependencies: - '@embroider/core': 3.4.16 + '@embroider/core': 3.4.16(@glint/template@1.4.0) '@rollup/pluginutils': 4.2.1 content-tag: 2.0.1 fs-extra: 10.1.0 @@ -1824,14 +1946,14 @@ packages: transitivePeerDependencies: - supports-color - /@embroider/core@3.4.16: + /@embroider/core@3.4.16(@glint/template@1.4.0): resolution: {integrity: sha512-M/Kk5PSi9GcdCFYFNiQLXY4Wz2Lw2nFe78lbfta0ntsM7xp/lig/mBRYAAwu/Vj4SgP3vUV8P2EFMBvGaE7qzg==} engines: {node: 12.* || 14.* || >= 16} dependencies: '@babel/core': 7.25.2 '@babel/parser': 7.25.6 '@babel/traverse': 7.25.6 - '@embroider/macros': 1.16.6 + '@embroider/macros': 1.16.6(@glint/template@1.4.0) '@embroider/shared-internals': 2.6.3 assert-never: 1.3.0 babel-plugin-ember-template-compilation: 2.3.0 @@ -1861,7 +1983,7 @@ packages: - utf-8-validate dev: true - /@embroider/macros@1.16.6: + /@embroider/macros@1.16.6(@glint/template@1.4.0): resolution: {integrity: sha512-aSdRetg0vY3c70G/3K85fOSlGtDzSV4ozwF9qD8ToQB+4RLZusxwItnctWEa+MKkhAYB6rbFiQ+bhFwEnaEazg==} engines: {node: 12.* || 14.* || >= 16} peerDependencies: @@ -1871,6 +1993,7 @@ packages: optional: true dependencies: '@embroider/shared-internals': 2.6.3 + '@glint/template': 1.4.0 assert-never: 1.3.0 babel-import-util: 2.1.1 ember-cli-babel: 7.26.11 @@ -2005,6 +2128,15 @@ packages: '@glimmer/interfaces': 0.92.0 '@glimmer/util': 0.92.0 + /@glimmer/destroyable@0.92.3: + resolution: {integrity: sha512-vQ+mzT9Vkf+JueY7L5XbZqK0WyEVTKv0HOLrw/zDw9F5Szn3F/8Ea/qbAClo3QK3oZeg+ulFTa/61rdjSFYHGA==} + dependencies: + '@glimmer/env': 0.1.7 + '@glimmer/global-context': 0.92.3 + '@glimmer/interfaces': 0.92.3 + '@glimmer/util': 0.92.3 + dev: true + /@glimmer/di@0.1.11: resolution: {integrity: sha512-moRwafNDwHTnTHzyyZC9D+mUSvYrs1Ak0tRPjjmCghdoHHIvMshVbEnwKb/1WmW5CUlKc2eL9rlAV32n3GiItg==} @@ -2026,6 +2158,10 @@ packages: /@glimmer/global-context@0.92.0: resolution: {integrity: sha512-XUPXIsz/F0YQz3vY9x+u3YQMibM3378gEPJObs3CHzAWJUl9Kz1CAb+jRigRrxIcmdzoonA49VMwGmmKRNoGag==} + /@glimmer/global-context@0.92.3: + resolution: {integrity: sha512-tvlK5pt6oSe3furJ1KsO9vG/KmF9S98HLrcR48XbfwXlkuxvUeS94cdQId4GCN5naeX4OC4xm6eEjZWdc2s+jw==} + dev: true + /@glimmer/interfaces@0.84.3: resolution: {integrity: sha512-dk32ykoNojt0mvEaIW6Vli5MGTbQo58uy3Epj7ahCgTHmWOKuw/0G83f2UmFprRwFx689YTXG38I/vbpltEjzg==} dependencies: @@ -2054,6 +2190,20 @@ packages: '@glimmer/validator': 0.92.0 '@glimmer/vm': 0.92.0 + /@glimmer/manager@0.92.4: + resolution: {integrity: sha512-YMoarZT/+Ft2YSd+Wuu5McVsdP9y6jeAdVQGYFpno3NlL3TXYbl7ELtK7OGxFLjzQE01BdiUZZRvcY+a/s9+CQ==} + dependencies: + '@glimmer/debug': 0.92.4 + '@glimmer/destroyable': 0.92.3 + '@glimmer/env': 0.1.7 + '@glimmer/global-context': 0.92.3 + '@glimmer/interfaces': 0.92.3 + '@glimmer/reference': 0.92.3 + '@glimmer/util': 0.92.3 + '@glimmer/validator': 0.92.3 + '@glimmer/vm': 0.92.3 + dev: true + /@glimmer/node@0.92.0: resolution: {integrity: sha512-TlyGmuCjGLWXvQDsAXUhDGjd4Q7BgNVwqv0hObu7A0qGOlEfpS1l6i/7cAzmCpQVUcGQiyUruJrIfpQgDWaepg==} dependencies: @@ -2076,11 +2226,32 @@ packages: '@glimmer/vm': 0.92.0 '@glimmer/wire-format': 0.92.3 + /@glimmer/opcode-compiler@0.92.4: + resolution: {integrity: sha512-WnZSBwxNqW/PPD/zfxEg6BVR5tHwTm8fp76piix8BNCQ6CuzVn6HUJ5SlvBsOwyoRCmzt/pkKmBJn+I675KG4w==} + dependencies: + '@glimmer/debug': 0.92.4 + '@glimmer/encoder': 0.92.3 + '@glimmer/env': 0.1.7 + '@glimmer/global-context': 0.92.3 + '@glimmer/interfaces': 0.92.3 + '@glimmer/manager': 0.92.4 + '@glimmer/reference': 0.92.3 + '@glimmer/util': 0.92.3 + '@glimmer/vm': 0.92.3 + '@glimmer/wire-format': 0.92.3 + dev: true + /@glimmer/owner@0.92.0: resolution: {integrity: sha512-SUhVaUvcLcVJ+9f8ob/fln0+z6jAinYv21sA1FcgAYMnb3eaB5RPjFFW3BjGy9VPT/IOAVyj95+NDm6wguMDEg==} dependencies: '@glimmer/util': 0.92.0 + /@glimmer/owner@0.92.3: + resolution: {integrity: sha512-ZxmXIUCy6DOobhGDhA6kMpaXZS7HAucEgIl/qcjV9crlzGOO8H4j+n2x6nA/8zpuqvO0gYaBzqdNdu+7EgOEmw==} + dependencies: + '@glimmer/util': 0.92.3 + dev: true + /@glimmer/program@0.92.0: resolution: {integrity: sha512-hRIZMRlRsyJuhUoqLsBu66NTPel6itXrccBOHBI49n9+FdisjiM3tgNNhrY+Tik/GnmtzztrCWjrqpf/PCp+rg==} dependencies: @@ -2093,6 +2264,19 @@ packages: '@glimmer/vm': 0.92.0 '@glimmer/wire-format': 0.92.3 + /@glimmer/program@0.92.4: + resolution: {integrity: sha512-fkquujQ11lsGCWl/+XpZW2E7bjHj/g6/Ht292A7pSoANBD8Bz/gPYiPM+XuMwes9MApEsTEMjV4EXlyk2/Cirg==} + dependencies: + '@glimmer/encoder': 0.92.3 + '@glimmer/env': 0.1.7 + '@glimmer/interfaces': 0.92.3 + '@glimmer/manager': 0.92.4 + '@glimmer/opcode-compiler': 0.92.4 + '@glimmer/util': 0.92.3 + '@glimmer/vm': 0.92.3 + '@glimmer/wire-format': 0.92.3 + dev: true + /@glimmer/reference@0.84.3: resolution: {integrity: sha512-lV+p/aWPVC8vUjmlvYVU7WQJsLh319SdXuAWoX/SE3pq340BJlAJiEcAc6q52y9JNhT57gMwtjMX96W5Xcx/qw==} dependencies: @@ -2112,6 +2296,16 @@ packages: '@glimmer/util': 0.92.0 '@glimmer/validator': 0.92.0 + /@glimmer/reference@0.92.3: + resolution: {integrity: sha512-Ud4LE689mEXL6BJnJx0ZPt2dt/A540C+TAnBFXHpcAjROz5gT337RN+tgajwudEUqpufExhcPSMGzs1pvWYCJg==} + dependencies: + '@glimmer/env': 0.1.7 + '@glimmer/global-context': 0.92.3 + '@glimmer/interfaces': 0.92.3 + '@glimmer/util': 0.92.3 + '@glimmer/validator': 0.92.3 + dev: true + /@glimmer/runtime@0.92.0: resolution: {integrity: sha512-LlAf86bNhRCfPvrXY5x+3YMhhSWSCT5NVTTYQp9j07D0bxvNw57n4mESuEgYZYWl4/cyEwegrmWW6Qs1P85bmQ==} dependencies: @@ -2128,6 +2322,23 @@ packages: '@glimmer/vm': 0.92.0 '@glimmer/wire-format': 0.92.3 + /@glimmer/runtime@0.92.4: + resolution: {integrity: sha512-ISqM/8hVh+fY/gnLAAPKfts4CvnJBOyCYAXgGccIlzzQrSVLaz0NoRiWTLGj5B/3xyPbqLwYPDvlTsOjYtvPoA==} + dependencies: + '@glimmer/destroyable': 0.92.3 + '@glimmer/env': 0.1.7 + '@glimmer/global-context': 0.92.3 + '@glimmer/interfaces': 0.92.3 + '@glimmer/manager': 0.92.4 + '@glimmer/owner': 0.92.3 + '@glimmer/program': 0.92.4 + '@glimmer/reference': 0.92.3 + '@glimmer/util': 0.92.3 + '@glimmer/validator': 0.92.3 + '@glimmer/vm': 0.92.3 + '@glimmer/wire-format': 0.92.3 + dev: true + /@glimmer/syntax@0.84.3: resolution: {integrity: sha512-ioVbTic6ZisLxqTgRBL2PCjYZTFIwobifCustrozRU2xGDiYvVIL0vt25h2c1ioDsX59UgVlDkIK4YTAQQSd2A==} dependencies: @@ -2203,6 +2414,15 @@ packages: '@glimmer/interfaces': 0.92.0 '@glimmer/util': 0.92.0 + /@glimmer/validator@0.92.3: + resolution: {integrity: sha512-HKrMYeW0YhiksSeKYqX2chUR/rz82j12DcY7p2dORQlTV3qlAfiE5zRTJH1KRA1X3ZMf7DI2/GOzkXwYp0o+3Q==} + dependencies: + '@glimmer/env': 0.1.7 + '@glimmer/global-context': 0.92.3 + '@glimmer/interfaces': 0.92.3 + '@glimmer/util': 0.92.3 + dev: true + /@glimmer/vm-babel-plugins@0.92.0(@babel/core@7.25.2): resolution: {integrity: sha512-s/jPlTykZb3YzzOCVmGyMP8NihonHM+eY5WBQl+MOCXe2KdGkTAxFgnuGYzHTtJ/JzCRa/YRXQhJhncJSg6L2A==} engines: {node: '>=16'} @@ -2229,6 +2449,95 @@ packages: '@glimmer/interfaces': 0.92.3 '@glimmer/util': 0.92.3 + /@glint/core@1.4.0(typescript@5.6.2): + resolution: {integrity: sha512-nq27a/1R6kc3lsuciz8z9IZO1NQCbNkEBxf5KJI7AUrnps6RzQzmq3pmwO24zQYmFcH4sqpod8fleNIpg8YEqg==} + hasBin: true + peerDependencies: + typescript: '>=4.8.0' + dependencies: + '@glimmer/syntax': 0.84.3 + escape-string-regexp: 4.0.0 + semver: 7.6.3 + silent-error: 1.1.1 + typescript: 5.6.2 + uuid: 8.3.2 + vscode-languageserver: 8.1.0 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.0.8 + yargs: 17.7.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@glint/environment-ember-loose@1.4.0(@glimmer/component@1.1.2)(@glint/template@1.4.0)(@types/ember__array@4.0.10)(@types/ember__component@4.0.22)(@types/ember__controller@4.0.12)(@types/ember__object@4.0.12)(@types/ember__routing@4.0.22)(ember-modifier@4.2.0): + resolution: {integrity: sha512-vFR3qgPTisGzS36e04195wTUrtUc6GuVwm6hsC/XXx6PeRw/6rtMxhK08Aw/VtDc00UqQzM9sIEghPVKHwqVVQ==} + peerDependencies: + '@glimmer/component': ^1.1.2 + '@glint/template': ^1.4.0 + '@types/ember__array': ^4.0.2 + '@types/ember__component': ^4.0.10 + '@types/ember__controller': ^4.0.2 + '@types/ember__object': ^4.0.4 + '@types/ember__routing': ^4.0.11 + ember-cli-htmlbars: ^6.0.1 + ember-modifier: ^3.2.7 || ^4.0.0 + peerDependenciesMeta: + '@types/ember__array': + optional: true + '@types/ember__component': + optional: true + '@types/ember__controller': + optional: true + '@types/ember__object': + optional: true + '@types/ember__routing': + optional: true + ember-cli-htmlbars: + optional: true + ember-modifier: + optional: true + dependencies: + '@glimmer/component': 1.1.2(@babel/core@7.25.2) + '@glint/template': 1.4.0 + '@types/ember__array': 4.0.10(@babel/core@7.25.2) + '@types/ember__component': 4.0.22(@babel/core@7.25.2) + '@types/ember__controller': 4.0.12(@babel/core@7.25.2) + '@types/ember__object': 4.0.12(@babel/core@7.25.2) + '@types/ember__routing': 4.0.22(@babel/core@7.25.2) + ember-modifier: 4.2.0(@babel/core@7.25.2)(ember-source@5.11.0) + dev: true + + /@glint/environment-ember-template-imports@1.4.0(@glint/environment-ember-loose@1.4.0)(@glint/template@1.4.0)(@types/ember__component@4.0.22)(@types/ember__helper@4.0.8)(@types/ember__modifier@4.0.9)(@types/ember__routing@4.0.22): + resolution: {integrity: sha512-VXcUgea92l7NFShU26rpQn+hYUZ7ex/rNtU9vnw2BAVZaPfxZROokW8ABj8aMaCUDe60CoMVZ1/QSeONSCln3w==} + peerDependencies: + '@glint/environment-ember-loose': ^1.4.0 + '@glint/template': ^1.4.0 + '@types/ember__component': ^4.0.10 + '@types/ember__helper': ^4.0.1 + '@types/ember__modifier': ^4.0.3 + '@types/ember__routing': ^4.0.12 + peerDependenciesMeta: + '@types/ember__component': + optional: true + '@types/ember__helper': + optional: true + '@types/ember__modifier': + optional: true + '@types/ember__routing': + optional: true + dependencies: + '@glint/environment-ember-loose': 1.4.0(@glimmer/component@1.1.2)(@glint/template@1.4.0)(@types/ember__array@4.0.10)(@types/ember__component@4.0.22)(@types/ember__controller@4.0.12)(@types/ember__object@4.0.12)(@types/ember__routing@4.0.22)(ember-modifier@4.2.0) + '@glint/template': 1.4.0 + '@types/ember__component': 4.0.22(@babel/core@7.25.2) + '@types/ember__helper': 4.0.8(@babel/core@7.25.2) + '@types/ember__modifier': 4.0.9(@babel/core@7.25.2) + '@types/ember__routing': 4.0.22(@babel/core@7.25.2) + content-tag: 2.0.1 + dev: true + + /@glint/template@1.4.0: + resolution: {integrity: sha512-yD271NhLei/HSQ6utm6hKgoU+B5D5DY+B1irPvgI4KsDEcZI7v/INf5HAMJfzCg92bP1sIxSOuXu5DU6VsY7Mw==} + /@gwhitney/detect-indent@7.0.1: resolution: {integrity: sha512-7bQW+gkKa2kKZPeJf6+c6gFK9ARxQfn+FKy9ScTBppyKRWH2KzsmweXUoklqeEiHiNVWaeP5csIdsNq6w7QhzA==} engines: {node: '>=12.20'} @@ -3029,6 +3338,10 @@ packages: rollup: 3.29.5 dev: true + /@rtsao/scc@1.1.0: + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + dev: true + /@scalvert/ember-setup-middleware-reporter@0.1.1: resolution: {integrity: sha512-C5DHU6YlKaISB5utGQ+jpsMB57ZtY0uZ8UkD29j855BjqG6eJ98lhA2h/BoJbyPw89RKLP1EEXroy9+5JPoyVw==} engines: {node: 12.* || >= 14} @@ -3075,6 +3388,10 @@ packages: engines: {node: '>= 6'} dev: true + /@tsconfig/ember@3.0.8: + resolution: {integrity: sha512-OVnIsZIt/8q0VEtcdz3rRryNrm6gdJTxXlxefkGIrkZnME0wqslmwHlUEZ7mvh377df9FqBhNKrYNarhCW8zJA==} + dev: true + /@types/body-parser@1.19.5: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: @@ -3108,6 +3425,199 @@ packages: '@types/node': 22.5.5 dev: true + /@types/ember@4.0.11(@babel/core@7.25.2): + resolution: {integrity: sha512-v7VIex0YILK8fP87LkIfzeeYKNnu74+xwf6U56v6MUDDGfSs9q/6NCxiUfwkxD+z5nQiUcwvfKVokX8qzZFRLw==} + dependencies: + '@types/ember__application': 4.0.11(@babel/core@7.25.2) + '@types/ember__array': 4.0.10(@babel/core@7.25.2) + '@types/ember__component': 4.0.22(@babel/core@7.25.2) + '@types/ember__controller': 4.0.12(@babel/core@7.25.2) + '@types/ember__debug': 4.0.8(@babel/core@7.25.2) + '@types/ember__engine': 4.0.11(@babel/core@7.25.2) + '@types/ember__error': 4.0.6 + '@types/ember__object': 4.0.12(@babel/core@7.25.2) + '@types/ember__polyfills': 4.0.6 + '@types/ember__routing': 4.0.22(@babel/core@7.25.2) + '@types/ember__runloop': 4.0.10(@babel/core@7.25.2) + '@types/ember__service': 4.0.9(@babel/core@7.25.2) + '@types/ember__string': 3.16.3 + '@types/ember__template': 4.0.7 + '@types/ember__test': 4.0.6(@babel/core@7.25.2) + '@types/ember__utils': 4.0.7(@babel/core@7.25.2) + '@types/rsvp': 4.0.9 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__application@4.0.11(@babel/core@7.25.2): + resolution: {integrity: sha512-U1S7XW0V70nTWbFckWoraJbYGBJK69muP/CsPFLeAuUYHfkkDiwh1SfqgAUN9aHtrEJM5SuSYVYp2YsTI2yLuA==} + dependencies: + '@glimmer/component': 1.1.2(@babel/core@7.25.2) + '@types/ember': 4.0.11(@babel/core@7.25.2) + '@types/ember__engine': 4.0.11(@babel/core@7.25.2) + '@types/ember__object': 4.0.12(@babel/core@7.25.2) + '@types/ember__owner': 4.0.9 + '@types/ember__routing': 4.0.22(@babel/core@7.25.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__array@4.0.10(@babel/core@7.25.2): + resolution: {integrity: sha512-UrhDbopLI3jB0MqV14y8yji2IuPNmeDrtT1PRYJL4CThLHrRkfeYyFvxqvrxWxn0wXKjbbjfH1gOe7BU57QrLQ==} + dependencies: + '@types/ember': 4.0.11(@babel/core@7.25.2) + '@types/ember__object': 4.0.12(@babel/core@7.25.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__component@4.0.22(@babel/core@7.25.2): + resolution: {integrity: sha512-m72EtmBN/RxOChXqRsyOg4RR5+AiB4LQ8s1CEKNYAfvANt18m4hjqxtA7QZYLTq2ZjEVJGpdMsrdDuONWjwRSQ==} + dependencies: + '@types/ember': 4.0.11(@babel/core@7.25.2) + '@types/ember__object': 4.0.12(@babel/core@7.25.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__controller@4.0.12(@babel/core@7.25.2): + resolution: {integrity: sha512-80rdnSC0UJBqoUX5/vkQcM2xkRdTPTvY0dPXEfY5cC5OZITbcSeRo5qa7ZGhgNBfH6XYyh55Lo/b811LwU3N9w==} + dependencies: + '@types/ember__object': 4.0.12(@babel/core@7.25.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__debug@4.0.8(@babel/core@7.25.2): + resolution: {integrity: sha512-9wF7STmDHDsUxSjyCq2lpMq/03QOPkBQMGJnV8yOBnVZxB6f+FJH/kxaCprdMkUe7iwAPNEC2zrFFx1tzH75Kg==} + dependencies: + '@types/ember__object': 4.0.12(@babel/core@7.25.2) + '@types/ember__owner': 4.0.9 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__destroyable@4.0.5: + resolution: {integrity: sha512-spJyZxpvecssbXkaOQYcbnlWgb+TasFaKrgAYVbykZY6saMwUdMOGDDoW6uP/y/+A8Jj/fUIatPWJLepeSfgww==} + dev: true + + /@types/ember__engine@4.0.11(@babel/core@7.25.2): + resolution: {integrity: sha512-ryR4Q1Xm3kQ3Ap58w10CxV3+vb3hs1cJqi7UZ5IlSdLRql7AbpS6hIjxSQ3EQ4zadeeJ6/D8JJcSwqR7eX3PFA==} + dependencies: + '@types/ember__object': 4.0.12(@babel/core@7.25.2) + '@types/ember__owner': 4.0.9 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__error@4.0.6: + resolution: {integrity: sha512-vYrLaGGjHkN14K89Vm8yqB2mkpJQefE5w7QJkkgYyV+smzns1vKlPbvuFevRtoeYNn4u4yY0JyF7HceNkm3H0Q==} + dev: true + + /@types/ember__helper@4.0.8(@babel/core@7.25.2): + resolution: {integrity: sha512-IhHkZHmwPx7zjabIr315qpQwgje+ASq5y6If1ZtRSwjm/0rL2Qa9FQu+h4cYW5jWdn2ptJ1pZZJTCe2uEVxsIQ==} + dependencies: + '@glimmer/manager': 0.92.4 + '@glimmer/runtime': 0.92.4 + '@types/ember': 4.0.11(@babel/core@7.25.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__modifier@4.0.9(@babel/core@7.25.2): + resolution: {integrity: sha512-npIlSh17198HHKKGtuVkhkGNKnFYJ4hpyy5ttmIEsPIm5N2zuNkl5YJd70i6j2ePLyKxpdfKz1GZqbJkrtTuVg==} + dependencies: + '@types/ember': 4.0.11(@babel/core@7.25.2) + '@types/ember__owner': 4.0.9 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__object@4.0.12(@babel/core@7.25.2): + resolution: {integrity: sha512-ZEpikPjZ02m1QCBiTPTayMJwVwF4UBlHlGDoScRB3IP/SUS1O5mmn1/CnSQDxzzF3ctfmhNuTZzVBBc1Y8OC1A==} + dependencies: + '@types/ember': 4.0.11(@babel/core@7.25.2) + '@types/rsvp': 4.0.9 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__owner@4.0.9: + resolution: {integrity: sha512-iyBda4aUIjBmeiKTKmPow/EJO7xWn8m85CnQTOCqQzTWJyJpgfObbXSHahOHXOfMm279Oa5NlbmS/EontB+XiQ==} + dev: true + + /@types/ember__polyfills@4.0.6: + resolution: {integrity: sha512-hbds3Qv+oVm/QKIaY1E6atvrCoJTH/MPSl4swOhX6P0RiMB2fOfFCrFSD1mP1KrU1LqpHJ2Rzs7XLe53SWVzgw==} + dev: true + + /@types/ember__routing@4.0.22(@babel/core@7.25.2): + resolution: {integrity: sha512-qLk9Vd2GMxdlGmX9xbzg4Farths+AQGzYDH901Wo2Nsre+Cwv1Tk1rbCiay2V3ICYZYufytdWT6V++DISF3nvw==} + dependencies: + '@types/ember': 4.0.11(@babel/core@7.25.2) + '@types/ember__controller': 4.0.12(@babel/core@7.25.2) + '@types/ember__object': 4.0.12(@babel/core@7.25.2) + '@types/ember__service': 4.0.9(@babel/core@7.25.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__runloop@4.0.10(@babel/core@7.25.2): + resolution: {integrity: sha512-9MZfOJBXuUP7RqLjovmzy1yY2xKTxVpqHMapqy6QJ8mjAekRmq9IJ+ni2zJ5CWftyb3Lqu3Eks05CL7fnbhcJA==} + dependencies: + '@types/ember': 4.0.11(@babel/core@7.25.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__service@4.0.9(@babel/core@7.25.2): + resolution: {integrity: sha512-DrepocL/4hH5YxbDWbxEKMDcAchBPSGGa4g+LEINW1Po81RmSdKw5GZV4UO0mvRWgkdu3EbWUxbTzB4gmbDSeQ==} + dependencies: + '@types/ember__object': 4.0.12(@babel/core@7.25.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__string@3.16.3: + resolution: {integrity: sha512-0T9ofzm9LL/bSG5u1SxKx/j2h/bHKkl5NKjGCNbFQxEKBw4f2cs6+AMDgWke9z+qrRRIz9vGEtMXnA3yJrO2xA==} + dependencies: + '@types/ember__template': 4.0.7 + dev: true + + /@types/ember__template@4.0.7: + resolution: {integrity: sha512-jv4hhG+8d1zdma+jhbCdJ3Ak7C22YNatGyWWvB3N9zbXq358AAPXaJoyNY8QTDbD/RIR9P6yoRk4u9vLbF6zfA==} + dev: true + + /@types/ember__test@4.0.6(@babel/core@7.25.2): + resolution: {integrity: sha512-Nswm/epfTepXknT8scZvWyyop1aqJcZcyzY4THGHFcXvYQQfA9rgmgrx6vo9vCJmYHh3jm0TTAIAIfoCvGaX5g==} + dependencies: + '@types/ember__application': 4.0.11(@babel/core@7.25.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + + /@types/ember__utils@4.0.7(@babel/core@7.25.2): + resolution: {integrity: sha512-qQPBeWRyIPigKnZ68POlkqI5e6XA78Q4G3xHo687wQTcEtfoL/iZyPC4hn70mdijcZq8Hjch2Y3E5yhsEMzK+g==} + dependencies: + '@types/ember': 4.0.11(@babel/core@7.25.2) + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + /@types/eslint@8.56.12: resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} dependencies: @@ -3173,6 +3683,10 @@ packages: /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + /@types/json5@0.0.29: + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + dev: true + /@types/keyv@3.1.4: resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} dependencies: @@ -3214,6 +3728,10 @@ packages: '@types/glob': 8.1.0 '@types/node': 22.5.5 + /@types/rsvp@4.0.9: + resolution: {integrity: sha512-F6vaN5mbxw2MBCu/AD9fSKwrhnto2pE77dyUsi415qz9IP9ni9ZOWXHxnXfsM4NW9UjW+it189jvvqnhv37Z7Q==} + dev: true + /@types/send@0.17.4: resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} dependencies: @@ -3238,6 +3756,132 @@ packages: /@types/symlink-or-copy@1.2.2: resolution: {integrity: sha512-MQ1AnmTLOncwEf9IVU+B2e4Hchrku5N67NkgcAHW0p3sdzPe0FNMANxEm6OJUzPniEQGkeT3OROLlCwZJLWFZA==} + /@typescript-eslint/eslint-plugin@8.6.0(@typescript-eslint/parser@8.6.0)(eslint@8.57.1)(typescript@5.6.2): + resolution: {integrity: sha512-UOaz/wFowmoh2G6Mr9gw60B1mm0MzUtm6Ic8G2yM1Le6gyj5Loi/N+O5mocugRGY+8OeeKmkMmbxNqUCq3B4Sg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.11.1 + '@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2) + '@typescript-eslint/scope-manager': 8.6.0 + '@typescript-eslint/type-utils': 8.6.0(eslint@8.57.1)(typescript@5.6.2) + '@typescript-eslint/utils': 8.6.0(eslint@8.57.1)(typescript@5.6.2) + '@typescript-eslint/visitor-keys': 8.6.0 + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.6.2) + typescript: 5.6.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@8.6.0(eslint@8.57.1)(typescript@5.6.2): + resolution: {integrity: sha512-eQcbCuA2Vmw45iGfcyG4y6rS7BhWfz9MQuk409WD47qMM+bKCGQWXxvoOs1DUp+T7UBMTtRTVT+kXr7Sh4O9Ow==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 8.6.0 + '@typescript-eslint/types': 8.6.0 + '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.6.2) + '@typescript-eslint/visitor-keys': 8.6.0 + debug: 4.3.7(supports-color@9.4.0) + eslint: 8.57.1 + typescript: 5.6.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@8.6.0: + resolution: {integrity: sha512-ZuoutoS5y9UOxKvpc/GkvF4cuEmpokda4wRg64JEia27wX+PysIE9q+lzDtlHHgblwUWwo5/Qn+/WyTUvDwBHw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@typescript-eslint/types': 8.6.0 + '@typescript-eslint/visitor-keys': 8.6.0 + dev: true + + /@typescript-eslint/type-utils@8.6.0(eslint@8.57.1)(typescript@5.6.2): + resolution: {integrity: sha512-dtePl4gsuenXVwC7dVNlb4mGDcKjDT/Ropsk4za/ouMBPplCLyznIaR+W65mvCvsyS97dymoBRrioEXI7k0XIg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.6.2) + '@typescript-eslint/utils': 8.6.0(eslint@8.57.1)(typescript@5.6.2) + debug: 4.3.7(supports-color@9.4.0) + ts-api-utils: 1.3.0(typescript@5.6.2) + typescript: 5.6.2 + transitivePeerDependencies: + - eslint + - supports-color + dev: true + + /@typescript-eslint/types@8.6.0: + resolution: {integrity: sha512-rojqFZGd4MQxw33SrOy09qIDS8WEldM8JWtKQLAjf/X5mGSeEFh5ixQlxssMNyPslVIk9yzWqXCsV2eFhYrYUw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dev: true + + /@typescript-eslint/typescript-estree@8.6.0(typescript@5.6.2): + resolution: {integrity: sha512-MOVAzsKJIPIlLK239l5s06YXjNqpKTVhBVDnqUumQJja5+Y94V3+4VUFRA0G60y2jNnTVwRCkhyGQpavfsbq/g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 8.6.0 + '@typescript-eslint/visitor-keys': 8.6.0 + debug: 4.3.7(supports-color@9.4.0) + fast-glob: 3.3.2 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.6.2) + typescript: 5.6.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@8.6.0(eslint@8.57.1)(typescript@5.6.2): + resolution: {integrity: sha512-eNp9cWnYf36NaOVjkEUznf6fEgVy1TWpE0o52e4wtojjBx7D1UV2WAWGzR+8Y5lVFtpMLPwNbC67T83DWSph4A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.6.0 + '@typescript-eslint/types': 8.6.0 + '@typescript-eslint/typescript-estree': 8.6.0(typescript@5.6.2) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@8.6.0: + resolution: {integrity: sha512-wapVFfZg9H0qOYh4grNVQiMklJGluQrOUiOhYRrQWhx7BY/+I1IYb8BczWNbbUpO+pqy0rDciv3lQH5E1bCLrg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + dependencies: + '@typescript-eslint/types': 8.6.0 + eslint-visitor-keys: 3.4.3 + dev: true + /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} dev: true @@ -3676,6 +4320,18 @@ packages: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} dev: true + /array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + get-intrinsic: 1.2.4 + is-string: 1.0.7 + dev: true + /array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} @@ -3686,6 +4342,38 @@ packages: engines: {node: '>=0.10.0'} dev: true + /array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + dev: true + + /array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-shim-unscopables: 1.0.2 + dev: true + /arraybuffer.prototype.slice@1.0.3: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} engines: {node: '>= 0.4'} @@ -5769,6 +6457,13 @@ packages: path-type: 4.0.0 dev: true + /doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + dependencies: + esutils: 2.0.3 + dev: true + /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -5877,7 +6572,7 @@ packages: '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.25.2) '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.25.2) '@babel/preset-env': 7.25.4(@babel/core@7.25.2) - '@embroider/macros': 1.16.6 + '@embroider/macros': 1.16.6(@glint/template@1.4.0) '@embroider/shared-internals': 2.6.3 babel-loader: 8.4.1(@babel/core@7.25.2)(webpack@5.94.0) babel-plugin-ember-modules-api-polyfill: 3.5.0 @@ -6397,12 +7092,7 @@ packages: - supports-color dev: true - /ember-disable-prototype-extensions@1.1.3: - resolution: {integrity: sha512-SB9NcZ27OtoUk+gfalsc3QU17+54OoqR668qHcuvHByk4KAhGxCKlkm9EBlKJcGr7yceOOAJqohTcCEBqfRw9g==} - engines: {node: '>= 0.10.0'} - dev: true - - /ember-eslint-parser@0.4.3(@babel/core@7.25.2)(eslint@8.57.1): + /ember-eslint-parser@0.4.3(@babel/core@7.25.2)(@typescript-eslint/parser@8.6.0)(eslint@8.57.1): resolution: {integrity: sha512-wMPoaaA+i/F/tPPxURRON9XXJH5MRUOZ5x/9CVJTSpL+0n4EWphyztb20gR+ZJeShnOACQpAdFy6YSS1/JSHKw==} engines: {node: '>=16.0.0'} peerDependencies: @@ -6415,6 +7105,7 @@ packages: '@babel/core': 7.25.2 '@babel/eslint-parser': 7.23.10(@babel/core@7.25.2)(eslint@8.57.1) '@glimmer/syntax': 0.92.3 + '@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2) content-tag: 1.2.2 eslint-scope: 7.2.2 html-tags: 3.3.1 @@ -6459,7 +7150,7 @@ packages: dependencies: '@ember/test-helpers': 4.0.4(@babel/core@7.25.2)(ember-source@5.11.0) '@embroider/addon-shim': 1.8.9 - '@embroider/macros': 1.16.6 + '@embroider/macros': 1.16.6(@glint/template@1.4.0) ember-cli-test-loader: 3.1.0 ember-source: 5.11.0(@glimmer/component@1.1.2)(webpack@5.94.0) qunit: 2.22.0 @@ -6870,6 +7561,12 @@ packages: has-tostringtag: 1.0.2 hasown: 2.0.2 + /es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + dependencies: + hasown: 2.0.2 + dev: true + /es-to-primitive@1.2.1: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} @@ -6940,7 +7637,46 @@ packages: resolution: {integrity: sha512-Uk/TVLt6Nf6Xoz7C1iYuZjOSdJxe5aaauGRke8JhKeJwD66Y61/pY2FjtLP04Ooq9PwV34bzrkKkU2UZ5FtDRA==} dev: true - /eslint-plugin-ember@12.2.0(@babel/core@7.25.2)(eslint@8.57.1): + /eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + dependencies: + debug: 3.2.7 + is-core-module: 2.15.1 + resolve: 1.22.8 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.11.0(@typescript-eslint/parser@8.6.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): + resolution: {integrity: sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2) + debug: 3.2.7 + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-plugin-ember@12.2.0(@babel/core@7.25.2)(@typescript-eslint/parser@8.6.0)(eslint@8.57.1): resolution: {integrity: sha512-Pf0LB70qzrGqbxrieASFDqxvGu7/xgejM78Kj+VsH27XqkuoluF1M5fBU5xxNB7oRCpA5IFA5jdN9WnnSjLzKA==} engines: {node: 18.* || 20.* || >= 21} peerDependencies: @@ -6951,8 +7687,9 @@ packages: optional: true dependencies: '@ember-data/rfc395-data': 0.0.4 + '@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2) css-tree: 2.3.1 - ember-eslint-parser: 0.4.3(@babel/core@7.25.2)(eslint@8.57.1) + ember-eslint-parser: 0.4.3(@babel/core@7.25.2)(@typescript-eslint/parser@8.6.0)(eslint@8.57.1) ember-rfc176-data: 0.3.18 eslint: 8.57.1 eslint-utils: 3.0.0(eslint@8.57.1) @@ -6977,6 +7714,42 @@ packages: eslint-compat-utils: 0.5.1(eslint@8.57.1) dev: true + /eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.6.0)(eslint@8.57.1): + resolution: {integrity: sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@rtsao/scc': 1.1.0 + '@typescript-eslint/parser': 8.6.0(eslint@8.57.1)(typescript@5.6.2) + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.6.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) + hasown: 2.0.2 + is-core-module: 2.15.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 + semver: 6.3.1 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + /eslint-plugin-n@17.10.3(eslint@8.57.1): resolution: {integrity: sha512-ySZBfKe49nQZWR1yFaA0v/GsH6Fgp8ah6XV0WDz6CN8WO0ek4McMzb7A2xnf4DCYV43frjCygvb9f/wx7UUxRw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -9753,11 +10526,6 @@ packages: readable-stream: 1.0.34 dev: true - /memorystream@0.3.1: - resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} - engines: {node: '>= 0.10.0'} - dev: true - /meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} @@ -10267,20 +11035,6 @@ packages: semver: 7.6.3 dev: true - /npm-run-all2@6.2.3: - resolution: {integrity: sha512-5RsxC7jEc/RjxOYBVdEfrJf5FsJ0pHA7jr2/OxrThXknajETCTYjigOCG3iaGjdYIKEQlDuCG0ir0T1HTva8pg==} - engines: {node: ^14.18.0 || ^16.13.0 || >=18.0.0, npm: '>= 8'} - hasBin: true - dependencies: - ansi-styles: 6.2.1 - cross-spawn: 7.0.3 - memorystream: 0.3.1 - minimatch: 9.0.5 - pidtree: 0.6.0 - read-package-json-fast: 3.0.2 - shell-quote: 1.8.1 - dev: true - /npm-run-path@2.0.2: resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} engines: {node: '>=4'} @@ -10362,6 +11116,25 @@ packages: has-symbols: 1.0.3 object-keys: 1.1.1 + /object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 + dev: true + + /object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + dev: true + /object.pick@1.3.0: resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} engines: {node: '>=0.10.0'} @@ -10369,6 +11142,15 @@ packages: isobject: 3.0.1 dev: true + /object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + dev: true + /on-finished@2.3.0: resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} engines: {node: '>= 0.8'} @@ -10777,12 +11559,6 @@ packages: engines: {node: '>=8.6'} dev: true - /pidtree@0.6.0: - resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} - engines: {node: '>=0.10'} - hasBin: true - dev: true - /pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} @@ -11180,14 +11956,6 @@ packages: strip-bom: 4.0.0 dev: true - /read-package-json-fast@3.0.2: - resolution: {integrity: sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dependencies: - json-parse-even-better-errors: 3.0.2 - npm-normalize-package-bin: 3.0.1 - dev: true - /read-yaml-file@2.1.0: resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} engines: {node: '>=10.13'} @@ -12370,6 +13138,11 @@ packages: ansi-regex: 6.1.0 dev: true + /strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + dev: true + /strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} @@ -12941,6 +13714,24 @@ packages: - supports-color dev: true + /ts-api-utils@1.3.0(typescript@5.6.2): + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + dependencies: + typescript: 5.6.2 + dev: true + + /tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + dev: true + /tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} dev: true @@ -13254,6 +14045,37 @@ packages: engines: {node: '>= 0.8'} dev: true + /vscode-jsonrpc@8.1.0: + resolution: {integrity: sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==} + engines: {node: '>=14.0.0'} + dev: true + + /vscode-languageserver-protocol@3.17.3: + resolution: {integrity: sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==} + dependencies: + vscode-jsonrpc: 8.1.0 + vscode-languageserver-types: 3.17.3 + dev: true + + /vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + dev: true + + /vscode-languageserver-types@3.17.3: + resolution: {integrity: sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==} + dev: true + + /vscode-languageserver@8.1.0: + resolution: {integrity: sha512-eUt8f1z2N2IEUDBsKaNapkz7jl5QpskN2Y0G01T/ItMxBxw1fJwvtySGB9QMecatne8jFIWJGWI61dWjyTLQsw==} + hasBin: true + dependencies: + vscode-languageserver-protocol: 3.17.3 + dev: true + + /vscode-uri@3.0.8: + resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} + dev: true + /w3c-hr-time@1.0.2: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} deprecated: Use your platform's native performance.now() and performance.timeOrigin. @@ -13775,8 +14597,12 @@ packages: '@ember/test-helpers': 4.0.4(@babel/core@7.25.2)(ember-source@5.11.0) '@ember/test-waiters': 3.1.0 '@embroider/addon-shim': 1.8.9 + '@embroider/macros': 1.16.6(@glint/template@1.4.0) + '@glimmer/env': 0.1.7 ember-modifier: 4.2.0(@babel/core@7.25.2)(ember-source@5.11.0) ember-source: 5.11.0(@glimmer/component@1.1.2)(webpack@5.94.0) + rsvp: 4.8.5 transitivePeerDependencies: + - '@glint/template' - supports-color dev: false diff --git a/test-app/package.json b/test-app/package.json index 7b220434..5e07fa59 100644 --- a/test-app/package.json +++ b/test-app/package.json @@ -49,7 +49,6 @@ "ember-cli-dependency-checker": "^3.3.2", "ember-cli-htmlbars": "^6.3.0", "ember-cli-inject-live-reload": "^2.1.0", - "ember-disable-prototype-extensions": "^1.1.3", "ember-load-initializers": "^2.1.2", "ember-modifier": "^4.2.0", "ember-qunit": "^8.1.0", diff --git a/test-app/tests/acceptance/smoke-modifier-test.js b/test-app/tests/acceptance/smoke-modifier-test.js index 16eb56e7..e975f5b0 100644 --- a/test-app/tests/acceptance/smoke-modifier-test.js +++ b/test-app/tests/acceptance/smoke-modifier-test.js @@ -1214,6 +1214,7 @@ module('Acceptance | smoke modifier', function (hooks) { function tableConditionalCellContents() { const elements = findAll('[data-test-fruits]'); let result = ''; + for (const index in elements) { const element = elements[index]; if (element.textContent) {