ember-sortable
Version:
Sortable UI primitives for Ember.
861 lines (810 loc) • 29.1 kB
JavaScript
import { _ as _applyDecoratedDescriptor, a as _defineProperty, b as _initializerDefineProperty } from '../_rollupPluginBabelHelpers-a7bede49.js';
import Modifier from 'ember-modifier';
import { computed, action, set } from '@ember/object';
import { isEnterKey, isSpaceKey, isDownArrowKey, isUpArrowKey, isLeftArrowKey, isRightArrowKey, isEscapeKey } from '../utils/keyboard.js';
import { ANNOUNCEMENT_ACTION_TYPES } from '../utils/constant.js';
import { defaultA11yAnnouncementConfig } from '../utils/defaults.js';
import { scheduleOnce, later, schedule, next } from '@ember/runloop';
import * as s from '@ember/service';
import { isDestroyed, registerDestructor } from '@ember/destroyable';
var _dec, _dec2, _class, _descriptor;
const service = s.service ?? s.inject;
const NO_MODEL = {};
/**
* Modifier to apply a11y support to a group container for the Sortable component
*
* @param {String} [a11yItemName] A name for each model, used for creating more meaningful a11y announcements.
* @param {Object} [a11yAnnouncementConfig] A map of action to function to build meaningful a11y announcements.
* @param {String} [itemVisualClass] A class for styling visual indicators on the yielded `sortable-item`.
* @param {Object} [handleVisualClass] An object for styling visual indicators on the yielded `sortable-handle` on different `move`.
* @param {Function} [onChange] An optional callback for when position rearrangements are confirmed.
*
* @module drag-drop/draggable-group
* @example
* <ol {{sortable-group onChange=this.update a11yAnnouncementConfig=this.myA11yConfig}}>
* {{#each model.items as |item|}}
* <li {{sortable-item model=item}}>
* {{item.name}}
* <span class="handle" {{sortable-handle}}>↕</span>
* </li>
* {{/each}}
* </ol>
*/
let SortableGroupModifier = (_dec = service('ember-sortable-internal-state'), _dec2 = computed('direction', 'sortedItems'), _class = class SortableGroupModifier extends Modifier {
/** End of keyboard utils */
get disabled() {
return this.named.disabled || false;
}
/** Start of a11y properties */
/**
* @property an object containing different classes for visual indicators
* @type {Object}
* @default null
* @example
* {
* UP: 'up'
* DOWN: 'down',
* LEFT: 'left',
* RIGHT: 'right',
* }
*/
get handleVisualClass() {
return this.named.handleVisualClass || NO_MODEL;
}
/**
* @property an object containing functions for producing screen reader announcements
* @type {Object}
* @example
* {
* MOVE: function() {},
* ACTIVATE: function() {},
* CONFIRM: function() {},
* CANCEL: function() {},
* }
*/
get a11yAnnouncementConfig() {
return this.named.a11yAnnouncementConfig || defaultA11yAnnouncementConfig;
}
get itemVisualClass() {
return this.named.itemVisualClass || 'is-activated';
}
get a11yItemName() {
return this.named.a11yItemName || 'item';
}
/** End of a11y properties */
/**
* Make sure that we cancel any ongoing keyboard operation when the focus is lost from the handle.
* Because this can be fired pre-maturely, effectively cancelling before other keyboard operations,
* we need to wait until other operations are completed, so this will cancel properly.
*
* @param {Event} event a DOM event.
*/
focusOut() {
if (!this.isRetainingFocus && !this._isElementWithinHandle(document.activeElement)) {
this.cancelKeyboardSelection();
}
}
/**
* Explanation
* 1. `KeyboardReorderMode` is disabled: users can activate it via ENTER or SPACE.
* 2. `KeyboardReorderMode` is enabled: users can reorder via UP or DOWN arrow keys. TODO: Expand to more keys, e.g LEFT, RIGHT
* 3. `KeyboardReorderMode` is enabled: users can finalize/save the reordering via ENTER or SPACE.
* 4. `KeyboardReorderMode` is enabled: users can discard the reordering via ESC.
*
* @param {Event} event a DOM event
*/
keyDown(event) {
if (!this.isKeyDownEnabled) {
return;
}
// Note: If handle is specified, we need to target the keyDown on the handle
const isKeyboardReorderModeEnabled = this.isKeyboardReorderModeEnabled;
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);
this._updateHandleVisualIndicators(_selectedItem, true);
this.isRetainingFocus = true;
scheduleOnce('render', this, () => {
this.element.focus();
this.isRetainingFocus = false;
});
// Prevent the default scroll
event.preventDefault();
return;
}
if (isKeyboardReorderModeEnabled) {
this._handleKeyboardReorder(event);
event.preventDefault();
}
}
/**
* Checks if the given element is a child of a handle.
*
* @param {Element} element a DOM element.
*/
_isElementWithinHandle(element) {
return !!element?.closest(`[data-sortable-handle]`);
}
/**
* Moves an sortedItem from one index to another index, effectively performing an reorder.
*
* @param {Integer} fromIndex the original index
* @param {Integer} toIndex the new index
*/
_move(fromIndex, toIndex) {
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';
// DOWN or RIGHT
if (toIndex > fromIndex) {
if (direction === 'grid') {
const valueX = item.x;
const valueY = item.y;
item.x = nextItem.x + (nextItem.width - item.width);
item.y = nextItem.y + (nextItem.height - item.height);
nextItem.x = valueX;
nextItem.y = valueY;
} else {
value = item[direction];
set(item, direction, nextItem[direction] + (nextItem[dimension] - item[dimension]));
set(nextItem, direction, value);
}
// UP or LEFT
} else {
if (direction === 'grid') {
const valueX = nextItem.x;
const valueY = nextItem.y;
nextItem.x = item.x + (item.width - nextItem.width);
nextItem.y = item.y + (item.height - nextItem.height);
item.x = valueX;
item.y = valueY;
} else {
value = nextItem[direction];
set(nextItem, direction, item[direction] + (item[dimension] - nextItem[dimension]));
set(item, direction, value);
}
}
}
/**
* Handles all of the keyboard operations, such as
* 1. Keyboard navigation for UP, DOWN, LEFT, RIGHT
* 2. Confirming reorder
* 3. Discard reorder
* 4. Also handles refocusing the element that triggered the interaction.
*
* @param {Event} event a DOM event.
*/
_handleKeyboardReorder(event) {
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)) {
this.moveItem(selectedItem, -1);
} else if ((direction === 'x' || direction === 'grid') && isLeftArrowKey(event)) {
this.moveItem(selectedItem, -1);
} else if ((direction === 'x' || direction === 'grid') && isRightArrowKey(event)) {
this.moveItem(selectedItem, 1);
} else if (isEnterKey(event) || isSpaceKey(event)) {
// confirm will reset the selectedItem, so caching it here before we remove it.
const itemElement = selectedItem.element;
this._announceAction(ANNOUNCEMENT_ACTION_TYPES.CONFIRM);
this.confirmKeyboardSelection();
this.isRetainingFocus = true;
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;
this._announceAction(ANNOUNCEMENT_ACTION_TYPES.CANCEL);
this.cancelKeyboardSelection();
this.isRetainingFocus = true;
scheduleOnce('render', this, () => {
const moves = this.moves;
if (moves && moves[0]) {
const sortedItems = this.sortedItems;
const fromIndex = moves[0][1];
const itemElement = sortedItems[fromIndex]?.element;
if (itemElement) {
this._focusItem(itemElement);
}
} else {
this._focusItem(_selectedItemElement);
}
this.isRetainingFocus = false;
});
}
}
/**
* Moves the item to its new position and adds the move to our history.
*
* @param {SortableItemModifier} item the item to be moved.
* @param {Integer} delta how much to move index-wise.
*/
moveItem(item, delta) {
const sortedItems = this.sortedItems;
const moves = this.moves;
const sortedIndex = sortedItems.indexOf(item);
const newSortedIndex = sortedIndex + delta;
// If out of bounds, we don't do anything.
if (newSortedIndex < 0 || newSortedIndex >= sortedItems.length) {
return;
}
this._announceAction(ANNOUNCEMENT_ACTION_TYPES.MOVE, delta);
// Guarantees that the before the UI is fully rendered before we move again.
scheduleOnce('render', this, () => {
this._move(sortedIndex, newSortedIndex);
this._updateHandleVisualIndicators(item, true);
moves.push([sortedIndex, newSortedIndex]);
});
}
/**
* Handles all the necessary operations needed for cancelling the current keyboard selection.
* 1. Disables keyboard reorder mode.
* 2. Undo all of the tracked moves.
* 3. Tears down the application container, so we are not focus locked within the application.
* 4. Resets the current selected item.
*/
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();
const fromIndex = move ? move[1] : 0;
const toIndex = move ? move[0] : 0;
this._move(fromIndex, toIndex);
}
this._tearDownA11yApplicationContainer();
this._updateItemVisualIndicators(_selectedItem, false);
this._updateHandleVisualIndicators(_selectedItem, false);
this._resetItemSelection();
}
/**
* Handles all th necessary operations needed for confirming the current keyboard selection.
* 1. Disables keyboard reorder mode.
* 2. Tears down the application container, so we are not focus locked within the container.
* 3. Make sure to update and sync all the internal items and UI.
* 4. Triggers the `onChange` action if provided.
* 5. Resets the currently selected item.
*/
confirmKeyboardSelection() {
const _selectedItem = this._selectedItem;
if (!_selectedItem) {
return;
}
this.moves = [];
this._disableKeyboardReorderMode();
this._tearDownA11yApplicationContainer();
set(_selectedItem, 'wasDropped', true);
this.commit();
this._updateItemVisualIndicators(_selectedItem, false);
this._updateHandleVisualIndicators(_selectedItem, false);
this._resetItemSelection();
}
/**
* Announces the message constructed from `a11yAnnouncementConfig`.
*
* @param {String} type the action type.
* @param {Number} delta how much distance (item-wise) is being moved.
*/
_announceAction(type, delta = 0) {
const a11yAnnouncementConfig = this.a11yAnnouncementConfig;
const a11yItemName = this.a11yItemName;
if (!a11yItemName || !(type in a11yAnnouncementConfig)) {
return;
}
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,
maxLength: sortedItems.length,
direction: this.direction,
delta
};
const message = a11yAnnouncementConfig[type](config);
announcer.textContent = message;
// Reset the message after the message is announced.
later(() => {
announcer.textContent = '';
}, 1000);
}
/**
* Reset the selected item.
*/
_resetItemSelection() {
this._selectedItem = null;
}
/**
* Updates the selected item's visual indicators.
*
* @param {SortableItemModifier} item the selected item.
* @param {Boolean} isActive to activate or deactivate the class.
*/
_updateItemVisualIndicators(item, isActive) {
const itemVisualClass = this.itemVisualClass;
if (!itemVisualClass || !item) {
return;
}
if (isActive) {
item.element.classList.add(itemVisualClass);
} else {
item.element.classList.remove(itemVisualClass);
}
}
/**
* Updates the selected item's handle's visual indicators
*
* @param {SortableItemModifier} item the selected item.
* @param {boolean} isUpdate to update or not update.
*/
_updateHandleVisualIndicators(item, isUpdate) {
const handleVisualClass = this.handleVisualClass;
if (handleVisualClass === NO_MODEL || !item) {
return;
}
const sortedItems = this.sortedItems;
const direction = this.direction;
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'];
visualKeys.forEach(visualKey => {
visualHandle.classList.remove(handleVisualClass[visualKey] ?? '');
});
if (!isUpdate) {
return;
}
if (index > 0 && visualKeys[0]) {
visualHandle.classList.add(handleVisualClass[visualKeys[0]] ?? '');
}
if (index < sortedItems.length - 1 && visualKeys[1]) {
visualHandle.classList.add(handleVisualClass[visualKeys[1]] ?? '');
}
}
/**
* Sets focus on the current item or its handle.
*
* @param {Element} itemElement an DOM element representing an sortable-item.
*/
_focusItem(itemElement) {
const handle = itemElement.querySelector('[data-sortable-handle]');
if (handle) {
handle.focus();
} else {
// The consumer did not use a handle, so we set focus back to the item.
itemElement.focus();
}
}
/**
* Enables keyboard reorder mode.
*/
_enableKeyboardReorderMode() {
this.isKeyboardReorderModeEnabled = true;
}
/**
* Disables keyboard reorder mode
*/
_disableKeyboardReorderMode() {
this.isKeyboardReorderModeEnabled = false;
}
/**
* Sets up the group as an application and make it programmatically focusable.
*/
_setupA11yApplicationContainer() {
this.element.setAttribute('role', 'application');
this.element.tabIndex = -1;
}
/**
* Tears down the `role=application` container.
*/
_tearDownA11yApplicationContainer() {
this.element.removeAttribute('role');
this.element.removeAttribute('tabIndex');
}
_prepareKeyboardReorderMode() {
this._enableKeyboardReorderMode();
this._setupA11yApplicationContainer();
}
// Begin of API
/**
@property direction
@type string
@default y
*/
get direction() {
return this.named.direction || 'y';
}
/**
Called when order of items has been changed
@property onChange
@type Function
@param {Object} groupModel group model (omitted if not set)
@param {Object[]} newModel models in their new order
@param {Object} itemModel model just dragged
@default null
*/
get onChange() {
return this.named.onChange;
}
/**
* This is the group name used to keep groups separate if there are more than one on the screen at a time.
* If no group is assigned a default is used
*
* @default "_EmberSortableGroup"
* @returns {*|string}
*/
get groupName() {
return this.named.groupName || '_EmberSortableGroup';
}
/**
This is an array of SortableItemModifiers
@property items
@type SortableItemModifier[]
*/
get items() {
return this._groupDef.items;
}
set(items) {
this._groupDef.items = items;
}
/**
* Announcer element
*
* @type {Element}
*/
/**
Position for the first item.
If spacing is present, first item's position will have to change as well.
@property firstItemPosition
@type Number
*/
get firstItemPosition() {
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
};
}
/**
An array of DOM elements.
@property sortedItems
@type SortableItemModifier[]
*/
get sortedItems() {
const direction = this.direction;
const groupStyles = getComputedStyle(this.element);
const groupWidth = parseFloat(groupStyles.width);
return this.items.sort((a, b) => {
if (direction === 'grid') {
const {
ax,
ay,
bx,
by
} = this._calculateGridPosition(a, b, groupWidth);
if (ay == by) return ax - bx;
return ay - by;
}
return a[direction] - b[direction];
});
}
/**
* Enables keyboard navigation
*/
activateKeyDown(selectedItem) {
this._selectedItem = selectedItem;
this.isKeyDownEnabled = true;
}
/**
* Disables keyboard navigation
* Currently used to handle keydown events bubbling up from
* elements that aren't meant to invoke keyboard navigation
* by ignoring them.
*/
deactivateKeyDown() {
this.isKeyDownEnabled = false;
}
/**
Register the group with this Sortable.
@method registerGroup
@param {SortableGroupModifier} group
*/
registerGroup(group) {
this._group = group;
}
/**
De-register the group with this Sortable.
@method deregisterGroup
@param {SortableGroupModifier} group
*/
deregisterGroup(group) {
if (this._group === group) {
this._group = null;
}
}
/**
Prepare for sorting.
Main purpose is to stash the current firstItemPosition so
we don’t incur expensive re-layouts.
@method _prepare
*/
prepare() {
this._firstItemPosition = this.firstItemPosition;
}
/**
Update item positions (relatively to the first element position).
@method update
@param {SortableItemModifier[]} sortedItems
*/
update(sortedItems) {
if (!sortedItems) {
sortedItems = this.sortedItems;
}
// Position of the first element
let axis = this._firstItemPosition;
// Just in case we haven’t called prepare first.
if (axis === undefined) {
axis = this.firstItemPosition;
}
const direction = this.direction;
let position = 0;
let groupPositionRight = 0;
let lastTopOffset = 0;
let maxPrevHeight = 0;
if (direction === 'grid') {
position = axis.x;
lastTopOffset = axis.y;
const groupStyles = getComputedStyle(this.element);
groupPositionRight = position + parseFloat(groupStyles.width);
} else {
position = axis[direction];
}
sortedItems.forEach(item => {
if (direction === 'grid' && position + item.width > groupPositionRight) {
lastTopOffset = lastTopOffset + maxPrevHeight;
position = axis.x;
maxPrevHeight = 0;
}
if (!isDestroyed(item) && !item.isDragging) {
if (direction === 'grid') {
item.x = position;
item.y = lastTopOffset;
} else {
set(item, direction, position);
}
}
// add additional spacing around active element
if (item.isBusy) {
position += item.spacing * 2;
}
if (direction === 'grid') {
if (item.height > maxPrevHeight) {
maxPrevHeight = item.height;
}
position += item.width;
}
if (direction === 'x') {
position += item.width;
}
if (direction === 'y') {
position += item.height;
}
});
}
/**
@method _commit
*/
commit() {
const items = this.sortedItems;
const itemModels = items.map(item => item.model);
const draggedItem = items.find(item => item.wasDropped);
let draggedModel;
if (draggedItem) {
draggedItem.wasDropped = false; // Reset
draggedModel = draggedItem.model;
}
this._updateItems();
this._onChange(itemModels, draggedModel);
}
_onChange(itemModels, draggedModel) {
if (this.onChange) {
this.onChange(itemModels, draggedModel);
}
}
/**
* Keeps the UI in sync with actual changes.
* Needed for drag and keyboard operations.
*/
_updateItems() {
const items = this.sortedItems;
delete this._firstItemPosition;
schedule('render', () => {
items.forEach(item => item.freeze());
});
schedule('afterRender', () => {
items.forEach(item => item.reset());
});
next(() => {
schedule('render', () => {
items.forEach(item => item.thaw());
});
});
}
_createAnnouncer() {
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;
const position = {
ax: a.x,
ay: a.y,
bx: b.x,
by: b.y
};
if (a.isDragging) {
const dragItemPos = this._calculateGridDragItemPos(position.ax, position.ay, position.bx, position.by, b.width, b.height, a.moveDirection, groupTopPos, groupLeftPos, groupWidth);
position.ax = dragItemPos.x;
position.ay = dragItemPos.y;
} else if (b.isDragging) {
const dragItemPos = this._calculateGridDragItemPos(position.bx, position.by, position.ax, position.ay, a.width, a.height, b.moveDirection, groupTopPos, groupLeftPos, groupWidth);
position.bx = dragItemPos.x;
position.by = dragItemPos.y;
}
// Math.hypot needs always a positive number (-5 = 5 in hypot).
// As a negative number will be positive, we need to fake position from non dragged element
if (a.isDragging && position.ax <= 0) {
position.ax = 0;
position.bx = 1;
}
if (b.isDragging && position.bx <= 0) {
position.bx = 0;
position.ax = 1;
}
return position;
}
_calculateGridDragItemPos(x, y, otherX, otherY, width, height, moveDirection, groupTopPos, groupLeftPos, groupWidth) {
const toleranceWidth = width / 4;
const initialX = x;
if (moveDirection.left) {
x = x - toleranceWidth;
}
if (moveDirection.right) {
x = x + toleranceWidth;
// Calculate the maximum of items in row & the maximal x-position of last item
const itemsPerRow = Math.floor(groupWidth / width);
const possibleLastItemPos = (itemsPerRow - 1) * width + groupLeftPos;
if (otherX > initialX && x + width > possibleLastItemPos - 1) {
// Removing one pixel is necessary to move drag item before other element
x = possibleLastItemPos - 1;
}
}
if (y < groupTopPos) {
y = groupTopPos;
}
const toleranceHeight = height / 4;
// When item is moved a quarter of height to top, user wants to move up
if (moveDirection.top && y - height + toleranceHeight <= otherY && y >= otherY) {
y = otherY;
// tolerance that it doesn't jump directly in previews line
} else if (moveDirection.top && y >= otherY - toleranceHeight && y <= otherY) {
y = otherY;
}
// When item is moved a quarter of height to bottom, user wants to move down
if (moveDirection.bottom && y <= otherY + height - toleranceHeight && y >= otherY) {
y = otherY;
// tolerance that it doesn't jump directly in next line
} else if (moveDirection.bottom && y > otherY - toleranceHeight && y <= otherY) {
y = otherY;
}
return {
x: x,
y: y
};
}
// end of API
addEventListener() {
this.element.addEventListener('keydown', this.keyDown);
this.element.addEventListener('focusout', this.focusOut);
}
removeEventListener() {
this.element.removeEventListener('keydown', this.keyDown);
this.element.removeEventListener('focusout', this.focusOut);
}
constructor(owner, args) {
super(owner, args);
/** Primary keyboard utils */
// Tracks the currently selected item
_defineProperty(this, "_selectedItem", null);
_defineProperty(this, "_group", null);
_defineProperty(this, "_firstItemPosition", void 0);
_defineProperty(this, "_groupDef", void 0);
// Tracks the current move
_defineProperty(this, "move", null);
_defineProperty(this, "moves", []);
// Tracks the status of keyboard reorder mode
_defineProperty(this, "isKeyboardReorderModeEnabled", false);
_defineProperty(this, "isKeyDownEnabled", false);
// Tracks if we're still performing a programmatic focus.
_defineProperty(this, "isRetainingFocus", false);
_initializerDefineProperty(this, "sortableService", _descriptor, this);
_defineProperty(this, "announcer", null);
_defineProperty(this, "element", void 0);
_defineProperty(this, "didSetup", false);
_defineProperty(this, "named", void 0);
registerDestructor(this, cleanup);
}
modify(element, _positional, named) {
this.element = element;
this.named = named;
this.removeEventListener();
if (!this.didSetup) {
this._groupDef = this.sortableService.fetchGroup(this.groupName);
this.announcer = this._createAnnouncer();
this.element.insertAdjacentElement('afterend', this.announcer);
this.sortableService.registerGroup(this.groupName, this);
this.didSetup = true;
}
if (this.disabled) {
return;
}
this.addEventListener();
}
}, _applyDecoratedDescriptor(_class.prototype, "focusOut", [action], Object.getOwnPropertyDescriptor(_class.prototype, "focusOut"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "keyDown", [action], Object.getOwnPropertyDescriptor(_class.prototype, "keyDown"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "cancelKeyboardSelection", [action], Object.getOwnPropertyDescriptor(_class.prototype, "cancelKeyboardSelection"), _class.prototype), _descriptor = _applyDecoratedDescriptor(_class.prototype, "sortableService", [_dec], {
configurable: true,
enumerable: true,
writable: true,
initializer: null
}), _applyDecoratedDescriptor(_class.prototype, "firstItemPosition", [_dec2], Object.getOwnPropertyDescriptor(_class.prototype, "firstItemPosition"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "activateKeyDown", [action], Object.getOwnPropertyDescriptor(_class.prototype, "activateKeyDown"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "deactivateKeyDown", [action], Object.getOwnPropertyDescriptor(_class.prototype, "deactivateKeyDown"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "registerGroup", [action], Object.getOwnPropertyDescriptor(_class.prototype, "registerGroup"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "deregisterGroup", [action], Object.getOwnPropertyDescriptor(_class.prototype, "deregisterGroup"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "prepare", [action], Object.getOwnPropertyDescriptor(_class.prototype, "prepare"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "update", [action], Object.getOwnPropertyDescriptor(_class.prototype, "update"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "commit", [action], Object.getOwnPropertyDescriptor(_class.prototype, "commit"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "_onChange", [action], Object.getOwnPropertyDescriptor(_class.prototype, "_onChange"), _class.prototype), _applyDecoratedDescriptor(_class.prototype, "_createAnnouncer", [action], Object.getOwnPropertyDescriptor(_class.prototype, "_createAnnouncer"), _class.prototype), _class);
/**
*
* @param {SortableGroupModifier} instance
*/
function cleanup(instance) {
// todo cleanup the announcer
if (instance.announcer?.parentNode) {
instance.announcer.parentNode.removeChild(instance.announcer);
}
instance.removeEventListener();
instance.sortableService.deregisterGroup(instance.groupName);
}
export { SortableGroupModifier as default };
//# sourceMappingURL=sortable-group.js.map