新增标签栏左右移动按钮
This commit is contained in:
parent
ed4427243d
commit
c25869e287
|
@ -1,37 +1,86 @@
|
|||
<template>
|
||||
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
|
||||
<slot/>
|
||||
</el-scrollbar>
|
||||
<div ref="scrollContainer" class="scroll-container" @wheel.prevent="handleScroll">
|
||||
<div ref="scrollWrapper" :style="{left: left + 'px'}" class="scroll-wrapper">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import createDetectElementResize from '@/vendor/detectElementResize'
|
||||
|
||||
const padding = 15 // tag's padding
|
||||
const scrollPanelLeft = 30
|
||||
|
||||
export default {
|
||||
name: 'ScrollPane',
|
||||
data() {
|
||||
return {
|
||||
left: 0
|
||||
left: scrollPanelLeft
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.detectElementResize = createDetectElementResize()
|
||||
this.detectElementResize.addResizeListener(this.$refs.scrollContainer, () => {
|
||||
const $container = this.$refs.scrollContainer
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $wrapper = this.$refs.scrollWrapper
|
||||
const $wrapperWidth = $wrapper.offsetWidth
|
||||
if ($containerWidth - padding > $wrapperWidth) {
|
||||
this.left = scrollPanelLeft
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
scrollLeft() {
|
||||
this.left = Math.min(scrollPanelLeft, this.left + 300)
|
||||
},
|
||||
scrollRight() {
|
||||
const $container = this.$refs.scrollContainer
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $wrapper = this.$refs.scrollWrapper
|
||||
const $wrapperWidth = $wrapper.offsetWidth
|
||||
if ($containerWidth - padding < $wrapperWidth) {
|
||||
this.left = Math.max(this.left - 300, $containerWidth - $wrapperWidth - padding - scrollPanelLeft)
|
||||
}
|
||||
},
|
||||
handleScroll(e) {
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
||||
const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap
|
||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||
const eventDelta = e.wheelDelta || -e.deltaY * 3
|
||||
const $container = this.$refs.scrollContainer
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $wrapper = this.$refs.scrollWrapper
|
||||
const $wrapperWidth = $wrapper.offsetWidth
|
||||
|
||||
if (eventDelta > 0) {
|
||||
this.left = Math.min(scrollPanelLeft, this.left + eventDelta)
|
||||
} else {
|
||||
if ($containerWidth - padding < $wrapperWidth) {
|
||||
if (this.left < -($wrapperWidth - $containerWidth + padding)) {
|
||||
this.left = this.left
|
||||
} else {
|
||||
this.left = Math.max(this.left + eventDelta, $containerWidth - $wrapperWidth - padding - scrollPanelLeft)
|
||||
}
|
||||
} else {
|
||||
this.left = scrollPanelLeft
|
||||
}
|
||||
}
|
||||
},
|
||||
moveToTarget($target) {
|
||||
const $container = this.$refs.scrollContainer.$el
|
||||
const $container = this.$refs.scrollContainer
|
||||
const $containerWidth = $container.offsetWidth
|
||||
const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap
|
||||
const $targetLeft = $target.offsetLeft
|
||||
const $targetWidth = $target.offsetWidth
|
||||
if ($targetLeft > $containerWidth) {
|
||||
// tag in the right
|
||||
$scrollWrapper.scrollLeft = $targetLeft - $containerWidth + $targetWidth + padding
|
||||
} else {
|
||||
|
||||
if ($targetLeft < -this.left) {
|
||||
// tag in the left
|
||||
$scrollWrapper.scrollLeft = $targetLeft - padding
|
||||
this.left = -$targetLeft + padding
|
||||
} else if ($targetLeft + padding > -this.left && $targetLeft + $targetWidth < -this.left + $containerWidth - padding) {
|
||||
// tag in the current view
|
||||
// eslint-disable-line
|
||||
} else {
|
||||
// tag in the right
|
||||
this.left = -($targetLeft - ($containerWidth - $targetWidth) + padding)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,18 +88,13 @@ export default {
|
|||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.scroll-container {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
/deep/ {
|
||||
.el-scrollbar__bar {
|
||||
bottom: 0px;
|
||||
}
|
||||
.el-scrollbar__wrap {
|
||||
height: 49px;
|
||||
.scroll-container {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
.scroll-wrapper {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
/* eslint-disable */
|
||||
export default function createDetectElementResize(nonce) {
|
||||
// Check `document` and `window` in case of server-side rendering
|
||||
var _window
|
||||
if (typeof window !== 'undefined') {
|
||||
_window = window
|
||||
} else if (typeof self !== 'undefined') {
|
||||
_window = self
|
||||
} else {
|
||||
_window = global
|
||||
}
|
||||
|
||||
var attachEvent = typeof document !== 'undefined' && document.attachEvent
|
||||
|
||||
if (!attachEvent) {
|
||||
var requestFrame = (function() {
|
||||
var raf =
|
||||
_window.requestAnimationFrame ||
|
||||
_window.mozRequestAnimationFrame ||
|
||||
_window.webkitRequestAnimationFrame ||
|
||||
function(fn) {
|
||||
return _window.setTimeout(fn, 20)
|
||||
}
|
||||
return function(fn) {
|
||||
return raf(fn)
|
||||
}
|
||||
})()
|
||||
|
||||
var cancelFrame = (function() {
|
||||
var cancel =
|
||||
_window.cancelAnimationFrame ||
|
||||
_window.mozCancelAnimationFrame ||
|
||||
_window.webkitCancelAnimationFrame ||
|
||||
_window.clearTimeout
|
||||
return function(id) {
|
||||
return cancel(id)
|
||||
}
|
||||
})()
|
||||
|
||||
var resetTriggers = function(element) {
|
||||
var triggers = element.__resizeTriggers__,
|
||||
expand = triggers.firstElementChild,
|
||||
contract = triggers.lastElementChild,
|
||||
expandChild = expand.firstElementChild
|
||||
contract.scrollLeft = contract.scrollWidth
|
||||
contract.scrollTop = contract.scrollHeight
|
||||
expandChild.style.width = expand.offsetWidth + 1 + 'px'
|
||||
expandChild.style.height = expand.offsetHeight + 1 + 'px'
|
||||
expand.scrollLeft = expand.scrollWidth
|
||||
expand.scrollTop = expand.scrollHeight
|
||||
}
|
||||
|
||||
var checkTriggers = function(element) {
|
||||
return (
|
||||
element.offsetWidth != element.__resizeLast__.width ||
|
||||
element.offsetHeight != element.__resizeLast__.height
|
||||
)
|
||||
}
|
||||
|
||||
var scrollListener = function(e) {
|
||||
// Don't measure (which forces) reflow for scrolls that happen inside of children!
|
||||
if (
|
||||
e.target.className.indexOf('contract-trigger') < 0 &&
|
||||
e.target.className.indexOf('expand-trigger') < 0
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
var element = this
|
||||
resetTriggers(this)
|
||||
if (this.__resizeRAF__) {
|
||||
cancelFrame(this.__resizeRAF__)
|
||||
}
|
||||
this.__resizeRAF__ = requestFrame(function() {
|
||||
if (checkTriggers(element)) {
|
||||
element.__resizeLast__.width = element.offsetWidth
|
||||
element.__resizeLast__.height = element.offsetHeight
|
||||
element.__resizeListeners__.forEach(function(fn) {
|
||||
fn.call(element, e)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* Detect CSS Animations support to detect element display/re-attach */
|
||||
var animation = false,
|
||||
keyframeprefix = '',
|
||||
animationstartevent = 'animationstart',
|
||||
domPrefixes = 'Webkit Moz O ms'.split(' '),
|
||||
startEvents = 'webkitAnimationStart animationstart oAnimationStart MSAnimationStart'.split(
|
||||
' ',
|
||||
),
|
||||
pfx = ''
|
||||
{
|
||||
var elm = document.createElement('fakeelement')
|
||||
if (elm.style.animationName !== undefined) {
|
||||
animation = true
|
||||
}
|
||||
|
||||
if (animation === false) {
|
||||
for (var i = 0; i < domPrefixes.length; i++) {
|
||||
if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) {
|
||||
pfx = domPrefixes[i]
|
||||
keyframeprefix = '-' + pfx.toLowerCase() + '-'
|
||||
animationstartevent = startEvents[i]
|
||||
animation = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var animationName = 'resizeanim'
|
||||
var animationKeyframes =
|
||||
'@' +
|
||||
keyframeprefix +
|
||||
'keyframes ' +
|
||||
animationName +
|
||||
' { from { opacity: 0; } to { opacity: 0; } } '
|
||||
var animationStyle =
|
||||
keyframeprefix + 'animation: 1ms ' + animationName + '; '
|
||||
}
|
||||
|
||||
var createStyles = function(doc) {
|
||||
if (!doc.getElementById('detectElementResize')) {
|
||||
// opacity:0 works around a chrome bug https://code.google.com/p/chromium/issues/detail?id=286360
|
||||
var css =
|
||||
(animationKeyframes || '') +
|
||||
'.resize-triggers { ' +
|
||||
(animationStyle || '') +
|
||||
'visibility: hidden; opacity: 0; } ' +
|
||||
'.resize-triggers, .resize-triggers > div, .contract-trigger:before { content: " "; display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; z-index: -1; } .resize-triggers > div { background: #eee; overflow: auto; } .contract-trigger:before { width: 200%; height: 200%; }',
|
||||
head = doc.head || doc.getElementsByTagName('head')[0],
|
||||
style = doc.createElement('style')
|
||||
|
||||
style.id = 'detectElementResize'
|
||||
style.type = 'text/css'
|
||||
|
||||
if (nonce != null) {
|
||||
style.setAttribute('nonce', nonce)
|
||||
}
|
||||
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = css
|
||||
} else {
|
||||
style.appendChild(doc.createTextNode(css))
|
||||
}
|
||||
|
||||
head.appendChild(style)
|
||||
}
|
||||
}
|
||||
|
||||
var addResizeListener = function(element, fn) {
|
||||
if (attachEvent) {
|
||||
element.attachEvent('onresize', fn)
|
||||
} else {
|
||||
if (!element.__resizeTriggers__) {
|
||||
var doc = element.ownerDocument
|
||||
var elementStyle = _window.getComputedStyle(element)
|
||||
if (elementStyle && elementStyle.position == 'static') {
|
||||
element.style.position = 'relative'
|
||||
}
|
||||
createStyles(doc)
|
||||
element.__resizeLast__ = {}
|
||||
element.__resizeListeners__ = [];
|
||||
(element.__resizeTriggers__ = doc.createElement('div')).className =
|
||||
'resize-triggers'
|
||||
element.__resizeTriggers__.innerHTML =
|
||||
'<div class="expand-trigger"><div></div></div>' +
|
||||
'<div class="contract-trigger"></div>'
|
||||
element.appendChild(element.__resizeTriggers__)
|
||||
resetTriggers(element)
|
||||
element.addEventListener('scroll', scrollListener, true)
|
||||
|
||||
/* Listen for a css animation to detect element display/re-attach */
|
||||
if (animationstartevent) {
|
||||
element.__resizeTriggers__.__animationListener__ = function animationListener(
|
||||
e,
|
||||
) {
|
||||
if (e.animationName == animationName) {
|
||||
resetTriggers(element)
|
||||
}
|
||||
}
|
||||
element.__resizeTriggers__.addEventListener(
|
||||
animationstartevent,
|
||||
element.__resizeTriggers__.__animationListener__,
|
||||
)
|
||||
}
|
||||
}
|
||||
element.__resizeListeners__.push(fn)
|
||||
}
|
||||
}
|
||||
|
||||
var removeResizeListener = function(element, fn) {
|
||||
if (attachEvent) {
|
||||
element.detachEvent('onresize', fn)
|
||||
} else {
|
||||
element.__resizeListeners__.splice(
|
||||
element.__resizeListeners__.indexOf(fn),
|
||||
1,
|
||||
)
|
||||
if (!element.__resizeListeners__.length) {
|
||||
element.removeEventListener('scroll', scrollListener, true)
|
||||
if (element.__resizeTriggers__.__animationListener__) {
|
||||
element.__resizeTriggers__.removeEventListener(
|
||||
animationstartevent,
|
||||
element.__resizeTriggers__.__animationListener__,
|
||||
)
|
||||
element.__resizeTriggers__.__animationListener__ = null
|
||||
}
|
||||
try {
|
||||
element.__resizeTriggers__ = !element.removeChild(
|
||||
element.__resizeTriggers__,
|
||||
)
|
||||
} catch (e) {
|
||||
// Preact compat; see developit/preact-compat/issues/228
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
addResizeListener,
|
||||
removeResizeListener
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
<template>
|
||||
<div class="tags-view-container">
|
||||
<span class="scroll-left-btn" @click="scrollLeft">
|
||||
<i class="el-icon-caret-left"/>
|
||||
</span>
|
||||
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
|
||||
<router-link
|
||||
v-for="tag in Array.from(visitedViews)"
|
||||
|
@ -13,6 +16,9 @@
|
|||
<span class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)"/>
|
||||
</router-link>
|
||||
</scroll-pane>
|
||||
<span class="scroll-right-btn" @click="scrollRight">
|
||||
<i class="el-icon-caret-right"/>
|
||||
</span>
|
||||
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
|
||||
<li @click="refreshSelectedTag(selectedTag)">{{ $t('tagsView.refresh') }}</li>
|
||||
<li @click="closeSelectedTag(selectedTag)">{{ $t('tagsView.close') }}</li>
|
||||
|
@ -58,6 +64,12 @@ export default {
|
|||
this.addViewTags()
|
||||
},
|
||||
methods: {
|
||||
scrollLeft() {
|
||||
this.$refs.scrollPane.scrollLeft()
|
||||
},
|
||||
scrollRight() {
|
||||
this.$refs.scrollPane.scrollRight()
|
||||
},
|
||||
generateTitle, // generateTitle by vue-i18n
|
||||
generateRoute() {
|
||||
if (this.$route.name) {
|
||||
|
@ -132,70 +144,84 @@ export default {
|
|||
</script>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
||||
.tags-view-container {
|
||||
height: 34px;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
||||
.tags-view-wrapper {
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
.tags-view-container {
|
||||
position: relative;
|
||||
.scroll-left-btn, .scroll-right-btn {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 33px;
|
||||
height: 33px;
|
||||
line-height: 33px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
z-index: 999;
|
||||
background-color: #fff;
|
||||
}
|
||||
.scroll-right-btn {
|
||||
left: auto;
|
||||
right: 0;
|
||||
}
|
||||
.tags-view-wrapper {
|
||||
position: relative;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: 1px solid #d8dce5;
|
||||
color: #495060;
|
||||
background: #fff;
|
||||
padding: 0 8px;
|
||||
height: 34px;
|
||||
border-bottom: 1px solid #d8dce5;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
||||
.tags-view-item {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: 1px solid #d8dce5;
|
||||
color: #495060;
|
||||
background: #fff;
|
||||
padding: 0 8px;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
margin-top: 4px;
|
||||
&:first-of-type {
|
||||
margin-left: 15px;
|
||||
}
|
||||
&.active {
|
||||
background-color: #42b983;
|
||||
color: #fff;
|
||||
border-color: #42b983;
|
||||
&::before {
|
||||
content: '';
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.contextmenu {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
margin-left: 5px;
|
||||
margin-top: 4px;
|
||||
&:first-of-type {
|
||||
margin-left: 15px;
|
||||
}
|
||||
&:last-of-type {
|
||||
margin-right: 15px;
|
||||
}
|
||||
&.active {
|
||||
background-color: #42b983;
|
||||
color: #fff;
|
||||
border-color: #42b983;
|
||||
&::before {
|
||||
content: '';
|
||||
background: #fff;
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
margin-right: 2px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 16px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.contextmenu {
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
z-index: 100;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 16px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style rel="stylesheet/scss" lang="scss">
|
||||
|
|
Loading…
Reference in New Issue