UNPKG

framework7

Version:

Full featured mobile HTML framework for building iOS & Android apps

513 lines 20.2 kB
import { getDocument } from 'ssr-window'; import $ from '../../shared/dom7.js'; import { nextFrame, bindMethods } from '../../shared/utils.js'; import { getSupport } from '../../shared/get-support.js'; const Swipeout = { init() { const app = this; const document = getDocument(); const touchesStart = {}; let isTouched; let isMoved; let isScrolling; let touchStartTime; let touchesDiff; let $swipeoutEl; let $swipeoutContent; let $actionsRight; let $actionsLeft; let actionsLeftWidth; let actionsRightWidth; let translate; let opened; let openedActionsSide; let $leftButtons; let $rightButtons; let direction; let $overswipeLeftButton; let $overswipeRightButton; let overswipeLeft; let overswipeRight; function handleTouchStart(e) { if (!app.swipeout.allow) return; isMoved = false; isTouched = true; isScrolling = undefined; touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX; touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY; touchStartTime = new Date().getTime(); $swipeoutEl = $(this); } function handleTouchMove(e) { if (!isTouched) return; const pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX; const pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY; if (typeof isScrolling === 'undefined') { isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x)); } if (isScrolling) { isTouched = false; return; } if (!isMoved) { if ($('.list.sortable-opened').length > 0) return; $swipeoutContent = $swipeoutEl.find('.swipeout-content'); $actionsRight = $swipeoutEl.find('.swipeout-actions-right'); $actionsLeft = $swipeoutEl.find('.swipeout-actions-left'); actionsLeftWidth = null; actionsRightWidth = null; $leftButtons = null; $rightButtons = null; $overswipeRightButton = null; $overswipeLeftButton = null; if ($actionsLeft.length > 0) { actionsLeftWidth = $actionsLeft.outerWidth(); $leftButtons = $actionsLeft.children('a'); $overswipeLeftButton = $actionsLeft.find('.swipeout-overswipe'); } if ($actionsRight.length > 0) { actionsRightWidth = $actionsRight.outerWidth(); $rightButtons = $actionsRight.children('a'); $overswipeRightButton = $actionsRight.find('.swipeout-overswipe'); } opened = $swipeoutEl.hasClass('swipeout-opened'); if (opened) { openedActionsSide = $swipeoutEl.find('.swipeout-actions-left.swipeout-actions-opened').length > 0 ? 'left' : 'right'; } $swipeoutEl.removeClass('swipeout-transitioning'); if (!app.params.swipeout.noFollow) { $swipeoutEl.find('.swipeout-actions-opened').removeClass('swipeout-actions-opened'); $swipeoutEl.removeClass('swipeout-opened'); } } isMoved = true; if (e.cancelable) { e.preventDefault(); } touchesDiff = pageX - touchesStart.x; translate = touchesDiff; if (opened) { if (openedActionsSide === 'right') translate -= actionsRightWidth;else translate += actionsLeftWidth; } if (translate > 0 && $actionsLeft.length === 0 || translate < 0 && $actionsRight.length === 0) { if (!opened) { isTouched = false; isMoved = false; $swipeoutContent.transform(''); if ($rightButtons && $rightButtons.length > 0) { $rightButtons.transform(''); } if ($leftButtons && $leftButtons.length > 0) { $leftButtons.transform(''); } return; } translate = 0; } if (translate < 0) direction = 'to-left';else if (translate > 0) direction = 'to-right';else if (!direction) direction = 'to-left'; let buttonOffset; let progress; e.f7PreventSwipePanel = true; if (app.params.swipeout.noFollow) { if (opened) { if (openedActionsSide === 'right' && touchesDiff > 0) { app.swipeout.close($swipeoutEl); } if (openedActionsSide === 'left' && touchesDiff < 0) { app.swipeout.close($swipeoutEl); } } else { if (touchesDiff < 0 && $actionsRight.length > 0) { app.swipeout.open($swipeoutEl, 'right'); } if (touchesDiff > 0 && $actionsLeft.length > 0) { app.swipeout.open($swipeoutEl, 'left'); } } isTouched = false; isMoved = false; return; } overswipeLeft = false; overswipeRight = false; if ($actionsRight.length > 0) { // Show right actions let buttonTranslate = translate; progress = buttonTranslate / actionsRightWidth; if (buttonTranslate < -actionsRightWidth) { const ratio = buttonTranslate / -actionsRightWidth; buttonTranslate = -actionsRightWidth - (-buttonTranslate - actionsRightWidth) ** 0.8; translate = buttonTranslate; if ($overswipeRightButton.length > 0 && ratio > app.params.swipeout.overswipeRatio) { overswipeRight = true; } } if (direction !== 'to-left') { progress = 0; buttonTranslate = 0; } $rightButtons.each(buttonEl => { const $buttonEl = $(buttonEl); if (typeof buttonEl.f7SwipeoutButtonOffset === 'undefined') { $buttonEl[0].f7SwipeoutButtonOffset = buttonEl.offsetLeft; } buttonOffset = buttonEl.f7SwipeoutButtonOffset; if ($overswipeRightButton.length > 0 && $buttonEl.hasClass('swipeout-overswipe') && direction === 'to-left') { $buttonEl.css({ left: `${overswipeRight ? -buttonOffset : 0}px` }); if (overswipeRight) { if (!$buttonEl.hasClass('swipeout-overswipe-active')) { $swipeoutEl.trigger('swipeout:overswipeenter'); app.emit('swipeoutOverswipeEnter', $swipeoutEl[0]); } $buttonEl.addClass('swipeout-overswipe-active'); } else { if ($buttonEl.hasClass('swipeout-overswipe-active')) { $swipeoutEl.trigger('swipeout:overswipeexit'); app.emit('swipeoutOverswipeExit', $swipeoutEl[0]); } $buttonEl.removeClass('swipeout-overswipe-active'); } } $buttonEl.transform(`translate3d(${buttonTranslate - buttonOffset * (1 + Math.max(progress, -1))}px,0,0)`); }); } if ($actionsLeft.length > 0) { // Show left actions let buttonTranslate = translate; progress = buttonTranslate / actionsLeftWidth; if (buttonTranslate > actionsLeftWidth) { const ratio = buttonTranslate / actionsRightWidth; buttonTranslate = actionsLeftWidth + (buttonTranslate - actionsLeftWidth) ** 0.8; translate = buttonTranslate; if ($overswipeLeftButton.length > 0 && ratio > app.params.swipeout.overswipeRatio) { overswipeLeft = true; } } if (direction !== 'to-right') { buttonTranslate = 0; progress = 0; } $leftButtons.each((buttonEl, index) => { const $buttonEl = $(buttonEl); if (typeof buttonEl.f7SwipeoutButtonOffset === 'undefined') { $buttonEl[0].f7SwipeoutButtonOffset = actionsLeftWidth - buttonEl.offsetLeft - buttonEl.offsetWidth; } buttonOffset = buttonEl.f7SwipeoutButtonOffset; if ($overswipeLeftButton.length > 0 && $buttonEl.hasClass('swipeout-overswipe') && direction === 'to-right') { $buttonEl.css({ left: `${overswipeLeft ? buttonOffset : 0}px` }); if (overswipeLeft) { if (!$buttonEl.hasClass('swipeout-overswipe-active')) { $swipeoutEl.trigger('swipeout:overswipeenter'); app.emit('swipeoutOverswipeEnter', $swipeoutEl[0]); } $buttonEl.addClass('swipeout-overswipe-active'); } else { if ($buttonEl.hasClass('swipeout-overswipe-active')) { $swipeoutEl.trigger('swipeout:overswipeexit'); app.emit('swipeoutOverswipeExit', $swipeoutEl[0]); } $buttonEl.removeClass('swipeout-overswipe-active'); } } if ($leftButtons.length > 1) { $buttonEl.css('z-index', $leftButtons.length - index); } $buttonEl.transform(`translate3d(${buttonTranslate + buttonOffset * (1 - Math.min(progress, 1))}px,0,0)`); }); } $swipeoutEl.trigger('swipeout', progress); app.emit('swipeout', $swipeoutEl[0], progress); $swipeoutContent.transform(`translate3d(${translate}px,0,0)`); } function handleTouchEnd() { if (!isTouched || !isMoved) { isTouched = false; isMoved = false; return; } isTouched = false; isMoved = false; const timeDiff = new Date().getTime() - touchStartTime; const $actions = direction === 'to-left' ? $actionsRight : $actionsLeft; const actionsWidth = direction === 'to-left' ? actionsRightWidth : actionsLeftWidth; let action; let $buttons; let i; if (timeDiff < 300 && (touchesDiff < -10 && direction === 'to-left' || touchesDiff > 10 && direction === 'to-right') || timeDiff >= 300 && Math.abs(translate) > actionsWidth / 2) { action = 'open'; } else { action = 'close'; } if (timeDiff < 300) { if (Math.abs(translate) === 0) action = 'close'; if (Math.abs(translate) === actionsWidth) action = 'open'; } if (action === 'open') { Swipeout.el = $swipeoutEl[0]; $swipeoutEl.trigger('swipeout:open'); app.emit('swipeoutOpen', $swipeoutEl[0]); $swipeoutEl.addClass('swipeout-opened swipeout-transitioning'); const newTranslate = direction === 'to-left' ? -actionsWidth : actionsWidth; $swipeoutContent.transform(`translate3d(${newTranslate}px,0,0)`); $actions.addClass('swipeout-actions-opened'); $buttons = direction === 'to-left' ? $rightButtons : $leftButtons; if ($buttons) { for (i = 0; i < $buttons.length; i += 1) { $($buttons[i]).transform(`translate3d(${newTranslate}px,0,0)`); } } if (overswipeRight) { $actionsRight.find('.swipeout-overswipe').trigger('click', 'f7Overswipe'); } if (overswipeLeft) { $actionsLeft.find('.swipeout-overswipe').trigger('click', 'f7Overswipe'); } } else { $swipeoutEl.trigger('swipeout:close'); app.emit('swipeoutClose', $swipeoutEl[0]); Swipeout.el = undefined; $swipeoutEl.addClass('swipeout-transitioning').removeClass('swipeout-opened'); $swipeoutContent.transform(''); $actions.removeClass('swipeout-actions-opened'); } let buttonOffset; if ($leftButtons && $leftButtons.length > 0 && $leftButtons !== $buttons) { $leftButtons.each(buttonEl => { const $buttonEl = $(buttonEl); buttonOffset = buttonEl.f7SwipeoutButtonOffset; if (typeof buttonOffset === 'undefined') { $buttonEl[0].f7SwipeoutButtonOffset = actionsLeftWidth - buttonEl.offsetLeft - buttonEl.offsetWidth; } $buttonEl.transform(`translate3d(${buttonOffset}px,0,0)`); }); } if ($rightButtons && $rightButtons.length > 0 && $rightButtons !== $buttons) { $rightButtons.each(buttonEl => { const $buttonEl = $(buttonEl); buttonOffset = buttonEl.f7SwipeoutButtonOffset; if (typeof buttonOffset === 'undefined') { $buttonEl[0].f7SwipeoutButtonOffset = buttonEl.offsetLeft; } $buttonEl.transform(`translate3d(${-buttonOffset}px,0,0)`); }); } $swipeoutContent.transitionEnd(() => { if (opened && action === 'open' || !opened && action === 'close') return; $swipeoutEl.trigger(action === 'open' ? 'swipeout:opened' : 'swipeout:closed'); app.emit(action === 'open' ? 'swipeoutOpened' : 'swipeoutClosed', $swipeoutEl[0]); $swipeoutEl.removeClass('swipeout-transitioning'); if (opened && action === 'close') { if ($actionsRight.length > 0) { $rightButtons.transform(''); } if ($actionsLeft.length > 0) { $leftButtons.transform(''); } } }); } const passiveListener = getSupport().passiveListener ? { passive: true } : false; app.on('touchstart', e => { if (Swipeout.el) { const $targetEl = $(e.target); if (!($(Swipeout.el).is($targetEl[0]) || $targetEl.parents('.swipeout').is(Swipeout.el) || $targetEl.hasClass('modal-in') || ($targetEl.attr('class') || '').indexOf('-backdrop') > 0 || $targetEl.hasClass('actions-modal') || $targetEl.parents('.actions-modal.modal-in, .dialog.modal-in').length > 0)) { app.swipeout.close(Swipeout.el); } } }); $(document).on(app.touchEvents.start, 'li.swipeout', handleTouchStart, passiveListener); app.on('touchmove:active', handleTouchMove); app.on('touchend:passive', handleTouchEnd); }, allow: true, el: undefined, open() { const app = this; for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } let [el, side, callback] = args; if (typeof args[1] === 'function') { [el, callback, side] = args; } const $el = $(el).eq(0); if ($el.length === 0) return; if (!$el.hasClass('swipeout') || $el.hasClass('swipeout-opened')) return; if (!side) { if ($el.find('.swipeout-actions-right').length > 0) side = 'right';else side = 'left'; } const $swipeoutActions = $el.find(`.swipeout-actions-${side}`); const $swipeoutContent = $el.find('.swipeout-content'); if ($swipeoutActions.length === 0) return; $el.trigger('swipeout:open').addClass('swipeout-opened').removeClass('swipeout-transitioning'); app.emit('swipeoutOpen', $el[0]); $swipeoutActions.addClass('swipeout-actions-opened'); const $buttons = $swipeoutActions.children('a'); const swipeoutActionsWidth = $swipeoutActions.outerWidth(); const translate = side === 'right' ? -swipeoutActionsWidth : swipeoutActionsWidth; if ($buttons.length > 1) { $buttons.each((buttonEl, buttonIndex) => { const $buttonEl = $(buttonEl); if (side === 'right') { $buttonEl.transform(`translate3d(${-buttonEl.offsetLeft}px,0,0)`); } else { $buttonEl.css('z-index', $buttons.length - buttonIndex).transform(`translate3d(${swipeoutActionsWidth - buttonEl.offsetWidth - buttonEl.offsetLeft}px,0,0)`); } }); } $el.addClass('swipeout-transitioning'); $swipeoutContent.transitionEnd(() => { $el.trigger('swipeout:opened'); app.emit('swipeoutOpened', $el[0]); if (callback) callback.call($el[0]); }); nextFrame(() => { $buttons.transform(`translate3d(${translate}px,0,0)`); $swipeoutContent.transform(`translate3d(${translate}px,0,0)`); }); Swipeout.el = $el[0]; }, close(el, callback) { const app = this; const $el = $(el).eq(0); if ($el.length === 0) return; if (!$el.hasClass('swipeout-opened')) return; const side = $el.find('.swipeout-actions-opened').hasClass('swipeout-actions-right') ? 'right' : 'left'; const $swipeoutActions = $el.find('.swipeout-actions-opened').removeClass('swipeout-actions-opened'); const $buttons = $swipeoutActions.children('a'); const swipeoutActionsWidth = $swipeoutActions.outerWidth(); app.swipeout.allow = false; $el.trigger('swipeout:close'); app.emit('swipeoutClose', $el[0]); $el.removeClass('swipeout-opened').addClass('swipeout-transitioning'); let closeTimeout; function onSwipeoutClose() { app.swipeout.allow = true; if ($el.hasClass('swipeout-opened')) return; $el.removeClass('swipeout-transitioning'); $buttons.transform(''); $el.trigger('swipeout:closed'); app.emit('swipeoutClosed', $el[0]); if (callback) callback.call($el[0]); if (closeTimeout) clearTimeout(closeTimeout); } $el.find('.swipeout-content').transform('').transitionEnd(onSwipeoutClose); closeTimeout = setTimeout(onSwipeoutClose, 500); $buttons.each(buttonEl => { const $buttonEl = $(buttonEl); if (side === 'right') { $buttonEl.transform(`translate3d(${-buttonEl.offsetLeft}px,0,0)`); } else { $buttonEl.transform(`translate3d(${swipeoutActionsWidth - buttonEl.offsetWidth - buttonEl.offsetLeft}px,0,0)`); } $buttonEl.css({ left: '0px' }).removeClass('swipeout-overswipe-active'); }); if (Swipeout.el && Swipeout.el === $el[0]) Swipeout.el = undefined; }, delete(el, callback) { const app = this; const $el = $(el).eq(0); if ($el.length === 0) return; Swipeout.el = undefined; $el.trigger('swipeout:delete'); app.emit('swipeoutDelete', $el[0]); $el.css({ height: `${$el.outerHeight()}px` }); $el.transitionEnd(() => { $el.trigger('swipeout:deleted'); app.emit('swipeoutDeleted', $el[0]); if (callback) callback.call($el[0]); if ($el.parents('.virtual-list').length > 0) { const virtualList = $el.parents('.virtual-list')[0].f7VirtualList; const virtualIndex = $el[0].f7VirtualListIndex; if (virtualList && typeof virtualIndex !== 'undefined') virtualList.deleteItem(virtualIndex); } else if (app.params.swipeout.removeElements) { if (app.params.swipeout.removeElementsWithTimeout) { setTimeout(() => { $el.remove(); }, app.params.swipeout.removeElementsTimeout); } else { $el.remove(); } } else { $el.removeClass('swipeout-deleting swipeout-transitioning'); } }); // eslint-disable-next-line // $el[0]._clientLeft = $el[0].clientLeft; nextFrame(() => { $el.addClass('swipeout-deleting swipeout-transitioning').css({ height: '0px' }).find('.swipeout-content').transform('translate3d(-100%,0,0)'); }); } }; export default { name: 'swipeout', params: { swipeout: { actionsNoFold: false, noFollow: false, removeElements: true, removeElementsWithTimeout: false, removeElementsTimeout: 0, overswipeRatio: 1.2 } }, create() { const app = this; bindMethods(app, { swipeout: Swipeout }); }, clicks: { '.swipeout-open': function openSwipeout($clickedEl, data) { if (data === void 0) { data = {}; } const app = this; app.swipeout.open(data.swipeout, data.side); }, '.swipeout-close': function closeSwipeout($clickedEl) { const app = this; const $swipeoutEl = $clickedEl.closest('.swipeout'); if ($swipeoutEl.length === 0) return; app.swipeout.close($swipeoutEl); }, '.swipeout-delete': function deleteSwipeout($clickedEl, data) { if (data === void 0) { data = {}; } const app = this; const $swipeoutEl = $clickedEl.closest('.swipeout'); if ($swipeoutEl.length === 0) return; const { confirm, confirmTitle } = data; if (data.confirm) { app.dialog.confirm(confirm, confirmTitle, () => { app.swipeout.delete($swipeoutEl); }); } else { app.swipeout.delete($swipeoutEl); } } }, on: { init() { const app = this; if (!app.params.swipeout) return; app.swipeout.init(); } } };