UNPKG

ember-sortable

Version:
326 lines (324 loc) 11.8 kB
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}}>&varr;</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 {};