ember-sortable
Version:
Sortable UI primitives for Ember.
326 lines (324 loc) • 11.8 kB
TypeScript
import Modifier from 'ember-modifier';
import { type A11yAnnouncementConfig } from '../utils/defaults.ts';
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';
export interface HandleVisualClass {
UP?: string;
DOWN?: string;
LEFT?: string;
RIGHT?: string;
}
interface Position {
x: number;
y: number;
}
export type TDirection = 'x' | 'y' | 'grid';
interface SortableGroupModifierSignature<T> {
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
*
* @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>
*/
export default class SortableGroupModifier<T> extends Modifier<SortableGroupModifierSignature<T>> {
/** Primary keyboard utils */
_selectedItem: SortableItemModifier<T> | null;
_group: SortableGroupModifier<T> | null;
_firstItemPosition?: Position;
_groupDef: Group<T>;
move: null;
moves: [number, number][];
isKeyboardReorderModeEnabled: boolean;
isKeyDownEnabled: boolean;
isRetainingFocus: boolean;
/** End of keyboard utils */
get disabled(): boolean;
/** 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(): HandleVisualClass;
/**
* @property an object containing functions for producing screen reader announcements
* @type {Object}
* @example
* {
* MOVE: function() {},
* ACTIVATE: function() {},
* CONFIRM: function() {},
* CANCEL: function() {},
* }
*/
get a11yAnnouncementConfig(): A11yAnnouncementConfig;
get itemVisualClass(): string;
get a11yItemName(): string;
/** 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(): void;
/**
* 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: KeyboardEvent): void;
/**
* Checks if the given element is a child of a handle.
*
* @param {Element} element a DOM element.
*/
_isElementWithinHandle(element: Element | null): boolean;
/**
* 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: number, toIndex: number): void;
/**
* 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: KeyboardEvent): void;
/**
* 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: SortableItemModifier<T>, delta: number): void;
/**
* 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(): void;
/**
* 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(): void;
/**
* 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: keyof A11yAnnouncementConfig, delta?: number): void;
/**
* Reset the selected item.
*/
_resetItemSelection(): void;
/**
* Updates the selected item's visual indicators.
*
* @param {SortableItemModifier} item the selected item.
* @param {Boolean} isActive to activate or deactivate the class.
*/
_updateItemVisualIndicators(item: SortableItemModifier<T>, isActive: boolean): void;
/**
* 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: SortableItemModifier<T>, isUpdate: boolean): void;
/**
* Sets focus on the current item or its handle.
*
* @param {Element} itemElement an DOM element representing an sortable-item.
*/
_focusItem(itemElement: HTMLElement): void;
/**
* Enables keyboard reorder mode.
*/
_enableKeyboardReorderMode(): void;
/**
* Disables keyboard reorder mode
*/
_disableKeyboardReorderMode(): void;
/**
* Sets up the group as an application and make it programmatically focusable.
*/
_setupA11yApplicationContainer(): void;
/**
* Tears down the `role=application` container.
*/
_tearDownA11yApplicationContainer(): void;
_prepareKeyboardReorderMode(): void;
/**
@property direction
@type string
@default y
*/
get direction(): TDirection;
/**
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(): (itemModels: T[], draggedModel: T | undefined) => void;
sortableService: EmberSortableService<T>;
/**
* 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(): string;
/**
This is an array of SortableItemModifiers
@property items
@type SortableItemModifier[]
*/
get items(): SortableItemModifier<T>[];
set(items: SortableItemModifier<T>[]): void;
/**
* Announcer element
*
* @type {Element}
*/
announcer: Element | null;
/**
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(): Position;
/**
An array of DOM elements.
@property sortedItems
@type SortableItemModifier[]
*/
get sortedItems(): SortableItemModifier<T>[];
/**
* Enables keyboard navigation
*/
activateKeyDown(selectedItem: SortableItemModifier<T>): void;
/**
* 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(): void;
/**
Register the group with this Sortable.
@method registerGroup
@param {SortableGroupModifier} group
*/
registerGroup(group: SortableGroupModifier<T>): void;
/**
De-register the group with this Sortable.
@method deregisterGroup
@param {SortableGroupModifier} group
*/
deregisterGroup(group: SortableGroupModifier<T>): void;
/**
Prepare for sorting.
Main purpose is to stash the current firstItemPosition so
we don’t incur expensive re-layouts.
@method _prepare
*/
prepare(): void;
/**
Update item positions (relatively to the first element position).
@method update
@param {SortableItemModifier[]} sortedItems
*/
update(sortedItems: SortableItemModifier<T>[]): void;
/**
@method _commit
*/
commit(): void;
_onChange(itemModels: T[], draggedModel: T | undefined): void;
/**
* Keeps the UI in sync with actual changes.
* Needed for drag and keyboard operations.
*/
_updateItems(): void;
_createAnnouncer(): HTMLSpanElement;
_calculateGridPosition(a: SortableItemModifier<T>, b: SortableItemModifier<T>, groupWidth: number): {
ax: number;
ay: number;
bx: number;
by: number;
};
_calculateGridDragItemPos(x: number, y: number, otherX: number, otherY: number, width: number, height: number, moveDirection: MoveDirection, groupTopPos: number, groupLeftPos: number, groupWidth: number): Position;
addEventListener(): void;
removeEventListener(): void;
element: HTMLElement;
didSetup: boolean;
named: NamedArgs<SortableGroupModifierSignature<T>>;
constructor(owner: Owner, args: ArgsFor<SortableGroupModifierSignature<T>>);
modify(element: HTMLElement, _positional: PositionalArgs<SortableGroupModifierSignature<T>>, named: NamedArgs<SortableGroupModifierSignature<T>>): void;
}
export {};