diff --git a/docs/tabs/detail/options.md b/docs/tabs/detail/options.md index 37b092b4..e7a446dc 100644 --- a/docs/tabs/detail/options.md +++ b/docs/tabs/detail/options.md @@ -99,6 +99,20 @@ `false` + + + +dragSort 2.12+ + + +是否开启标签项拖拽排序功能 + + +boolean + + +`false` + diff --git a/src/css/layui.css b/src/css/layui.css index 14a14745..59d9a4a2 100644 --- a/src/css/layui.css +++ b/src/css/layui.css @@ -1315,6 +1315,8 @@ body .layui-table-tips .layui-layer-content{background: none; padding: 0; box-sh .layui-tabs-scroll .layui-tabs-header:after{display: none; content: none; border: 0;} .layui-tabs-bar .layui-icon{position: absolute; left: 0; top: 0; z-index: 3; width: 40px; height: 100%; line-height: 40px; border: 1px solid #eee; text-align: center; cursor: pointer; box-sizing: border-box; background-color: #fff; box-shadow: 2px 0 5px 0 rgb(0 0 0 / 6%);} .layui-tabs-bar .layui-icon-next{left: auto; right: 0; box-shadow: -2px 0 5px 0 rgb(0 0 0 / 6%);} +.layui-tabs-dragsort-chosen:before {content: ''; height: 100%; width: 2px; background-color: #16baaa; position: absolute; top: 0; left: 0; box-sizing: border-box;} +.layui-tabs-dragsort-chosen *{ pointer-events: none;} .layui-tabs-header li .layui-tabs-close{position: relative; display: inline-block; width: 16px; height: 16px; line-height: 18px; margin-left: 8px; top: 0px; text-align: center; font-size: 12px; color: #959595; border-radius: 50%; font-weight: 700; transition: all .16s; -webkit-transition: all .16s;} .layui-tabs-header li .layui-tabs-close:hover{ background-color: #ff5722; color: #fff;} diff --git a/src/modules/tabs.js b/src/modules/tabs.js index ca188943..b592468e 100644 --- a/src/modules/tabs.js +++ b/src/modules/tabs.js @@ -25,7 +25,8 @@ layui.define('component', function(exports) { CLOSE: 'layui-tabs-close', BODY: 'layui-tabs-body', ITEM: 'layui-tabs-item', - CARD: 'layui-tabs-card' + CARD: 'layui-tabs-card', + DRAG_CHOSEN: 'layui-tabs-dragsort-chosen' }, // 渲染 @@ -151,11 +152,17 @@ layui.define('component', function(exports) { }); inner.onresize = true; } + + // 拖拽事件 + if(options.dragSort){ + that.dragSort(container.header.elem); + } } }); // 内部变量集 var inner = {}; + var rAF = window.requestAnimationFrame || function(fn){return setTimeout(fn, 1000 / 60)}; /** * 扩展组件原型方法 @@ -363,9 +370,10 @@ layui.define('component', function(exports) { * 切换标签 * @param {Object} thisHeaderItem - 当前标签头部项元素 * @param {boolean} [force=false] - 是否强制切换 + * @param {boolean} [disabledScroll=false] - 是否禁用滚动 * @returns */ - Class.prototype.change = function(thisHeaderItem, force) { + Class.prototype.change = function(thisHeaderItem, force, disabledScroll) { if (!thisHeaderItem || !thisHeaderItem[0]) return; var that = this; @@ -418,7 +426,10 @@ layui.define('component', function(exports) { that.findBodyItem(layid || index).addClass(component.CONST.CLASS_SHOW) .siblings().removeClass(component.CONST.CLASS_SHOW); - that.roll('auto', index); + // 标签切换 + if (!disabledScroll) { + that.roll('auto', index); + } // 重新获取标签相关数据 var data = that.data(); @@ -714,6 +725,111 @@ layui.define('component', function(exports) { }; }; + /** + * 拖拽排序 + * @param {jQuery} headerElem - 拖拽的头部容器元素 + */ + Class.prototype.dragSort = function(headerElem) { + var that = this; + var namespace = '.lay-tabs-drag'; + var container = headerElem.parent(); + var scrollable = container.hasClass('layui-tabs-scroll'); + var draggedElem; + var scrollWidth = headerElem.prop('scrollWidth'); + var outerWidth = headerElem.outerWidth(); + var scrollThreshold = 100; + + var isDndElem = function(el) { + return el && el.nodeName.toLowerCase() == 'li' && !!el.getAttribute('lay-id'); + }; + + headerElem.off(namespace); + headerElem.on('mousedown' + namespace, '>li', function() { + if(isDndElem(this)){ + this.draggable = true; + } + }); + headerElem.on('dragstart' + namespace, function(e) { + e.originalEvent.dataTransfer.effectAllowed = 'move'; + draggedElem = $(e.target); + that.change(draggedElem, false, true); + }); + headerElem.on('dragenter' + namespace, function(e) { + if(isDndElem(e.target)) { + $(e.target).addClass(component.CONST.DRAG_CHOSEN); + } + }); + headerElem.on("dragover" + namespace, function (e) { + e.preventDefault(); + e.originalEvent.dataTransfer.dropEffect = "move"; + + // 拖拽边缘滚动 + if (scrollable) { + var tabLeft = headerElem.data("left") || 0; + var containerRect = container[0].getBoundingClientRect(); + var step = 200; + var rAFStep = 5; + var isScrollLeft = e.clientX - containerRect.left < scrollThreshold; + var isScrollRight = containerRect.right - e.clientX < scrollThreshold; + + if (!isScrollLeft && !isScrollRight) { + return; + } + + var cb = function () { + if (step > 0) { + step -= rAFStep; + if (isScrollLeft) { + tabLeft += rAFStep; + } else if (isScrollRight) { + tabLeft += -rAFStep; + } + if (tabLeft > 0) { + tabLeft = 0; + } else if (tabLeft < outerWidth - scrollWidth) { + tabLeft = outerWidth - scrollWidth; + } + headerElem.css("left", tabLeft).data("left", tabLeft); + rAF(cb); + } + }; + rAF(cb); + } + }); + headerElem.on('dragleave' + namespace, function(e) { + if(isDndElem(e.target)) { + $(e.target).removeClass(component.CONST.DRAG_CHOSEN); + } + }); + headerElem.on('drop' + namespace, function(e) { + e.preventDefault(); + if (isDndElem(e.target)) { + var dropTargetElem = $(e.target); + dropTargetElem.removeClass(component.CONST.DRAG_CHOSEN); + var dragIndex = that.data().thisHeaderItem.index(); + var dropIndex = dropTargetElem.index(); + + if(dragIndex > dropIndex) { + dropTargetElem.before(draggedElem); + } else { + dropTargetElem.after(draggedElem); + } + } + }); + headerElem.on('dragend' + namespace, function(e) { + e.preventDefault(); + draggedElem = null; + + var data = that.data(); + layui.event.call( + data.thisHeaderItem[0], + component.CONST.MOD_NAME, + 'dragend(' + that.config.id +')', + that.data() + ); + }); + }; + // 扩展组件接口 $.extend(component, { /**