UNPKG

devextreme

Version:

JavaScript/TypeScript Component Suite for Responsive Web Development

268 lines (267 loc) • 11 kB
/** * DevExtreme (esm/__internal/ui/list/list.edit.decorator.switchable.slide.js) * Version: 25.2.7 * Build date: Tue May 05 2026 * * Copyright (c) 2012 - 2026 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ import { fx } from "../../../common/core/animation"; import { locate, move } from "../../../common/core/animation/translator"; import { name as clickEventName } from "../../../common/core/events/click"; import { active } from "../../../common/core/events/core/emitter.feedback"; import eventsEngine from "../../../common/core/events/core/events_engine"; import { addNamespace } from "../../../common/core/events/utils"; import messageLocalization from "../../../common/core/localization/message"; import $ from "../../../core/renderer"; import { noop } from "../../../core/utils/common"; import { getOuterWidth, setWidth } from "../../../core/utils/size"; import { current, isMaterialBased } from "../../../ui/themes"; import ActionSheet from "../../ui/action_sheet"; import SwitchableEditDecorator from "../../ui/list/list.edit.decorator.switchable"; import { register as registerDecorator } from "../../ui/list/list.edit.decorator_registry"; const LIST_EDIT_DECORATOR = "dxListEditDecorator"; const CLICK_EVENT_NAME = addNamespace(clickEventName, LIST_EDIT_DECORATOR); const ACTIVE_EVENT_NAME = addNamespace(active, LIST_EDIT_DECORATOR); const SLIDE_MENU_CLASS = "dx-list-slide-menu"; const SLIDE_MENU_WRAPPER_CLASS = "dx-list-slide-menu-wrapper"; const SLIDE_MENU_CONTENT_CLASS = "dx-list-slide-menu-content"; const SLIDE_MENU_BUTTONS_CONTAINER_CLASS = "dx-list-slide-menu-buttons-container"; const SLIDE_MENU_BUTTONS_CLASS = "dx-list-slide-menu-buttons"; const SLIDE_MENU_BUTTON_CLASS = "dx-list-slide-menu-button"; const SLIDE_MENU_BUTTON_MENU_CLASS = "dx-list-slide-menu-button-menu"; const SLIDE_MENU_BUTTON_DELETE_CLASS = "dx-list-slide-menu-button-delete"; const SLIDE_MENU_ANIMATION_DURATION = 400; const SLIDE_MENU_ANIMATION_EASING = "cubic-bezier(0.075, 0.82, 0.165, 1)"; class SwitchableEditDecoratorSlide extends SwitchableEditDecorator { _shouldHandleSwipe() { return true } _init() { super._init(); this._$buttonsContainer = $("<div>").addClass(SLIDE_MENU_BUTTONS_CONTAINER_CLASS); eventsEngine.on(this._$buttonsContainer, ACTIVE_EVENT_NAME, noop); this._$buttons = $("<div>").addClass(SLIDE_MENU_BUTTONS_CLASS).appendTo(this._$buttonsContainer); this._renderMenu(); this._renderDeleteButton() } _renderMenu() { const { menuItems: menuItems = [] } = this._list.option(); if (!menuItems.length) { return } if (1 === menuItems.length) { const menuItem = menuItems[0]; this._renderMenuButton(menuItem.text ?? "", e => { e.stopPropagation(); this._fireAction(menuItem) }) } else { const $menu = $("<div>").addClass(SLIDE_MENU_CLASS); this._menu = this._list._createComponent($menu, ActionSheet, { showTitle: false, items: menuItems, onItemClick: args => { this._fireAction(args.itemData) }, integrationOptions: {} }); $menu.appendTo(this._list.$element()); const $menuButton = this._renderMenuButton(messageLocalization.format("dxListEditDecorator-more"), e => { e.stopPropagation(); this._menu.show() }); this._menu.option("target", $menuButton) } } _renderMenuButton(text, action) { const $menuButton = $("<div>").addClass(SLIDE_MENU_BUTTON_CLASS).addClass(SLIDE_MENU_BUTTON_MENU_CLASS).text(text); this._$buttons.append($menuButton); eventsEngine.on($menuButton, CLICK_EVENT_NAME, action); return $menuButton } _renderDeleteButton() { const { allowItemDeleting: allowItemDeleting } = this._list.option(); if (!allowItemDeleting) { return } const $deleteButton = $("<div>").addClass(SLIDE_MENU_BUTTON_CLASS).addClass(SLIDE_MENU_BUTTON_DELETE_CLASS).text(isMaterialBased(current()) ? "" : messageLocalization.format("dxListEditDecorator-delete")); eventsEngine.on($deleteButton, CLICK_EVENT_NAME, e => { e.stopPropagation(); this._deleteItem() }); this._$buttons.append($deleteButton) } _fireAction(menuItem) { this._list._itemEventHandlerByHandler($(this._cachedNode), menuItem.action, {}, { excludeValidators: ["disabled", "readOnly"] }); this._cancelDeleteReadyItem() } modifyElement(config) { super.modifyElement(config); const { $itemElement: $itemElement } = config; $itemElement.addClass(SLIDE_MENU_WRAPPER_CLASS); const $slideMenuContent = $("<div>").addClass(SLIDE_MENU_CONTENT_CLASS); $itemElement.wrapInner($slideMenuContent) } _getDeleteButtonContainer() { return this._$buttonsContainer } handleClick($itemElement, e) { if ($(e.target).closest(`.${SLIDE_MENU_CONTENT_CLASS}`).length) { return super.handleClick($itemElement, e) } return false } _swipeStartHandler($itemElement) { this._enablePositioning($itemElement); this._cacheItemData($itemElement); this._setPositions(this._getPositions(0)) } _swipeUpdateHandler($itemElement, e) { const rtl = this._isRtlEnabled(); const signCorrection = rtl ? -1 : 1; const isItemReadyToDelete = this._isReadyToDelete($itemElement); const moveJustStarted = this._getCurrentPositions().content === this._getStartPositions().content; if (moveJustStarted && !isItemReadyToDelete && e.offset * signCorrection > 0) { e.cancel = true; return } const offset = this._cachedItemWidth * e.offset; const startOffset = isItemReadyToDelete ? -this._cachedButtonWidth * signCorrection : 0; const correctedOffset = (offset + startOffset) * signCorrection; const percent = correctedOffset < 0 ? Math.abs((offset + startOffset) / this._cachedButtonWidth) : 0; this._setPositions(this._getPositions(percent)) } _getStartPositions() { const rtl = this._isRtlEnabled(); const signCorrection = rtl ? -1 : 1; return { content: 0, buttonsContainer: rtl ? -this._cachedButtonWidth : this._cachedItemWidth, buttons: -this._cachedButtonWidth * signCorrection } } _getPositions(percent) { const rtl = this._isRtlEnabled(); const signCorrection = rtl ? -1 : 1; const startPositions = this._getStartPositions(); return { content: startPositions.content - percent * this._cachedButtonWidth * signCorrection, buttonsContainer: startPositions.buttonsContainer - Math.min(percent, 1) * this._cachedButtonWidth * signCorrection, buttons: startPositions.buttons + Math.min(percent, 1) * this._cachedButtonWidth * signCorrection } } _getCurrentPositions() { return { content: locate(this._$cachedContent).left, buttonsContainer: locate(this._$buttonsContainer).left, buttons: locate(this._$buttons).left } } _setPositions(positions) { move(this._$cachedContent, { left: positions.content }); move(this._$buttonsContainer, { left: positions.buttonsContainer }); move(this._$buttons, { left: positions.buttons }) } _cacheItemData($itemElement) { var _this$_$cachedContent; if ($itemElement[0] === this._cachedNode) { return } this._$cachedContent = $itemElement.find(`.${SLIDE_MENU_CONTENT_CLASS}`); this._cachedItemWidth = getOuterWidth($itemElement); this._cachedButtonWidth = this._cachedButtonWidth || getOuterWidth(this._$buttons); setWidth(this._$buttonsContainer, this._cachedButtonWidth); if (null !== (_this$_$cachedContent = this._$cachedContent) && void 0 !== _this$_$cachedContent && _this$_$cachedContent.length) { this._cachedNode = $itemElement.get(0) } } _minButtonContainerLeftOffset() { return this._cachedItemWidth - this._cachedButtonWidth } _swipeEndHandler($itemElement, args) { this._cacheItemData($itemElement); const signCorrection = this._isRtlEnabled() ? 1 : -1; const offset = this._cachedItemWidth * args.offset; const endedAtReadyToDelete = !this._isReadyToDelete($itemElement) && offset * signCorrection > .2 * this._cachedButtonWidth; const readyToDelete = args.targetOffset === signCorrection && endedAtReadyToDelete; this._toggleDeleteReady($itemElement, readyToDelete) } _enablePositioning($itemElement) { if (this._$cachedContent) { fx.stop(this._$cachedContent.get(0), true) } super._enablePositioning($itemElement); this._$buttonsContainer.appendTo($itemElement) } _disablePositioning($itemElement) { super._disablePositioning($itemElement); this._$buttonsContainer.detach() } _animatePrepareDeleteReady() { return this._animateToPositions(this._getPositions(1)) } _animateForgetDeleteReady($itemElement) { this._cacheItemData($itemElement); return this._animateToPositions(this._getPositions(0)) } _animateToPositions(positions) { const currentPosition = this._getCurrentPositions(); const durationTimePart = Math.min(Math.abs(currentPosition.content - positions.content) / this._cachedButtonWidth, 1); return fx.animate($(this._$cachedContent).get(0), { from: currentPosition, to: positions, easing: SLIDE_MENU_ANIMATION_EASING, duration: 400 * durationTimePart, strategy: "frame", draw: drawPositions => { this._setPositions(drawPositions) } }) } dispose() { if (this._menu) { this._menu.$element().remove() } if (this._$buttonsContainer) { this._$buttonsContainer.remove() } super.dispose() } } registerDecorator("menu", "slide", SwitchableEditDecoratorSlide);