primevue-mirror/components/lib/scrollpanel/ScrollPanel.vue

378 lines
14 KiB
Vue
Raw Normal View History

2022-09-06 12:03:37 +00:00
<template>
2024-02-11 23:48:21 +00:00
<div :class="cx('root')" v-bind="ptmi('root')">
2023-05-24 14:08:05 +00:00
<div :class="cx('wrapper')" v-bind="ptm('wrapper')">
2023-12-07 06:49:59 +00:00
<div ref="content" :id="contentId" :class="cx('content')" @scroll="onScroll" @mouseenter="moveBar" v-bind="ptm('content')">
2022-09-06 12:03:37 +00:00
<slot></slot>
</div>
</div>
2022-09-14 11:26:01 +00:00
<div
ref="xBar"
2023-05-24 14:08:05 +00:00
:class="cx('barx')"
2022-09-14 11:26:01 +00:00
tabindex="0"
role="scrollbar"
aria-orientation="horizontal"
2023-12-07 06:49:59 +00:00
:aria-controls="contentId"
2022-09-14 11:26:01 +00:00
:aria-valuenow="lastScrollLeft"
@mousedown="onXBarMouseDown"
@keydown="onKeyDown($event)"
@keyup="onKeyUp"
@focus="onFocus"
@blur="onBlur"
2023-03-27 06:27:52 +00:00
v-bind="ptm('barx')"
2023-07-17 09:39:35 +00:00
data-pc-group-section="bar"
2022-09-14 11:26:01 +00:00
></div>
<div
ref="yBar"
2023-05-24 14:08:05 +00:00
:class="cx('bary')"
2022-09-14 11:26:01 +00:00
tabindex="0"
role="scrollbar"
aria-orientation="vertical"
2023-12-07 06:49:59 +00:00
:aria-controls="contentId"
2022-09-14 11:26:01 +00:00
:aria-valuenow="lastScrollTop"
@mousedown="onYBarMouseDown"
@keydown="onKeyDown($event)"
@keyup="onKeyUp"
@focus="onFocus"
2023-03-27 06:27:52 +00:00
v-bind="ptm('bary')"
2023-07-17 09:39:35 +00:00
data-pc-group-section="bar"
2022-09-14 11:26:01 +00:00
></div>
2022-09-06 12:03:37 +00:00
</div>
</template>
<script>
2022-09-14 11:26:01 +00:00
import { DomHandler, UniqueComponentId } from 'primevue/utils';
2023-05-29 09:19:55 +00:00
import BaseScrollPanel from './BaseScrollPanel.vue';
2022-09-06 12:03:37 +00:00
export default {
name: 'ScrollPanel',
2023-05-24 14:08:05 +00:00
extends: BaseScrollPanel,
2024-02-11 23:48:21 +00:00
inheritAttrs: false,
2022-09-06 12:03:37 +00:00
initialized: false,
documentResizeListener: null,
documentMouseMoveListener: null,
documentMouseUpListener: null,
frame: null,
scrollXRatio: null,
scrollYRatio: null,
isXBarClicked: false,
isYBarClicked: false,
lastPageX: null,
lastPageY: null,
2022-09-14 11:26:01 +00:00
timer: null,
outsideClickListener: null,
data() {
return {
2024-02-02 15:47:51 +00:00
id: this.$attrs.id,
2022-09-14 11:26:01 +00:00
orientation: 'vertical',
lastScrollTop: 0,
lastScrollLeft: 0
};
},
2024-02-02 15:47:51 +00:00
watch: {
2024-04-16 09:35:02 +00:00
'$attrs.id': function (newValue) {
this.id = newValue || UniqueComponentId();
2024-03-27 23:27:46 +00:00
}
2024-02-02 15:47:51 +00:00
},
2022-09-06 12:03:37 +00:00
mounted() {
2024-04-16 09:35:02 +00:00
this.id = this.id || UniqueComponentId();
2022-09-06 12:03:37 +00:00
if (this.$el.offsetParent) {
this.initialize();
}
},
updated() {
if (!this.initialized && this.$el.offsetParent) {
this.initialize();
}
},
beforeUnmount() {
this.unbindDocumentResizeListener();
if (this.frame) {
window.cancelAnimationFrame(this.frame);
}
},
methods: {
initialize() {
this.moveBar();
this.bindDocumentResizeListener();
this.calculateContainerHeight();
},
calculateContainerHeight() {
let containerStyles = getComputedStyle(this.$el),
2022-09-14 11:26:01 +00:00
xBarStyles = getComputedStyle(this.$refs.xBar),
pureContainerHeight = DomHandler.getHeight(this.$el) - parseInt(xBarStyles['height'], 10);
2022-09-06 12:03:37 +00:00
2022-09-14 11:26:01 +00:00
if (containerStyles['max-height'] !== 'none' && pureContainerHeight === 0) {
if (this.$refs.content.offsetHeight + parseInt(xBarStyles['height'], 10) > parseInt(containerStyles['max-height'], 10)) {
2022-09-06 12:03:37 +00:00
this.$el.style.height = containerStyles['max-height'];
2022-09-14 11:26:01 +00:00
} else {
this.$el.style.height =
this.$refs.content.offsetHeight + parseFloat(containerStyles.paddingTop) + parseFloat(containerStyles.paddingBottom) + parseFloat(containerStyles.borderTopWidth) + parseFloat(containerStyles.borderBottomWidth) + 'px';
2022-09-06 12:03:37 +00:00
}
}
},
moveBar() {
if (this.$refs.content) {
/* horizontal scroll */
let totalWidth = this.$refs.content.scrollWidth;
let ownWidth = this.$refs.content.clientWidth;
let bottom = (this.$el.clientHeight - this.$refs.xBar.clientHeight) * -1;
2022-09-06 12:03:37 +00:00
this.scrollXRatio = ownWidth / totalWidth;
2022-09-06 12:03:37 +00:00
/* vertical scroll */
let totalHeight = this.$refs.content.scrollHeight;
let ownHeight = this.$refs.content.clientHeight;
let right = (this.$el.clientWidth - this.$refs.yBar.clientWidth) * -1;
2022-09-06 12:03:37 +00:00
this.scrollYRatio = ownHeight / totalHeight;
2022-09-06 12:03:37 +00:00
this.frame = this.requestAnimationFrame(() => {
if (this.scrollXRatio >= 1) {
this.$refs.xBar.setAttribute('data-p-scrollpanel-hidden', 'true');
!this.isUnstyled && DomHandler.addClass(this.$refs.xBar, 'p-scrollpanel-hidden');
} else {
this.$refs.xBar.setAttribute('data-p-scrollpanel-hidden', 'false');
!this.isUnstyled && DomHandler.removeClass(this.$refs.xBar, 'p-scrollpanel-hidden');
this.$refs.xBar.style.cssText = 'width:' + Math.max(this.scrollXRatio * 100, 10) + '%; left:' + (this.$refs.content.scrollLeft / totalWidth) * 100 + '%;bottom:' + bottom + 'px;';
}
2022-09-06 12:03:37 +00:00
if (this.scrollYRatio >= 1) {
this.$refs.yBar.setAttribute('data-p-scrollpanel-hidden', 'true');
!this.isUnstyled && DomHandler.addClass(this.$refs.yBar, 'p-scrollpanel-hidden');
} else {
this.$refs.yBar.setAttribute('data-p-scrollpanel-hidden', 'false');
!this.isUnstyled && DomHandler.removeClass(this.$refs.yBar, 'p-scrollpanel-hidden');
this.$refs.yBar.style.cssText = 'height:' + Math.max(this.scrollYRatio * 100, 10) + '%; top: calc(' + (this.$refs.content.scrollTop / totalHeight) * 100 + '% - ' + this.$refs.xBar.clientHeight + 'px);right:' + right + 'px;';
}
});
}
2022-09-06 12:03:37 +00:00
},
onYBarMouseDown(e) {
this.isYBarClicked = true;
2022-09-14 11:26:01 +00:00
this.$refs.yBar.focus();
2022-09-06 12:03:37 +00:00
this.lastPageY = e.pageY;
this.$refs.yBar.setAttribute('data-p-scrollpanel-grabbed', 'true');
2023-05-24 14:08:05 +00:00
!this.isUnstyled && DomHandler.addClass(this.$refs.yBar, 'p-scrollpanel-grabbed');
document.body.setAttribute('data-p-scrollpanel-grabbed', 'true');
2023-05-24 14:08:05 +00:00
!this.isUnstyled && DomHandler.addClass(document.body, 'p-scrollpanel-grabbed');
2022-09-06 12:03:37 +00:00
this.bindDocumentMouseListeners();
e.preventDefault();
},
onXBarMouseDown(e) {
this.isXBarClicked = true;
2022-09-14 11:26:01 +00:00
this.$refs.xBar.focus();
2022-09-06 12:03:37 +00:00
this.lastPageX = e.pageX;
this.$refs.yBar.setAttribute('data-p-scrollpanel-grabbed', 'false');
2023-05-24 14:08:05 +00:00
!this.isUnstyled && DomHandler.addClass(this.$refs.xBar, 'p-scrollpanel-grabbed');
document.body.setAttribute('data-p-scrollpanel-grabbed', 'false');
2023-05-24 14:08:05 +00:00
!this.isUnstyled && DomHandler.addClass(document.body, 'p-scrollpanel-grabbed');
2022-09-06 12:03:37 +00:00
this.bindDocumentMouseListeners();
e.preventDefault();
},
2022-09-14 11:26:01 +00:00
onScroll(event) {
if (this.lastScrollLeft !== event.target.scrollLeft) {
this.lastScrollLeft = event.target.scrollLeft;
this.orientation = 'horizontal';
} else if (this.lastScrollTop !== event.target.scrollTop) {
this.lastScrollTop = event.target.scrollTop;
this.orientation = 'vertical';
}
this.moveBar();
},
onKeyDown(event) {
if (this.orientation === 'vertical') {
switch (event.code) {
case 'ArrowDown': {
this.setTimer('scrollTop', this.step);
event.preventDefault();
break;
}
case 'ArrowUp': {
this.setTimer('scrollTop', this.step * -1);
event.preventDefault();
break;
}
case 'ArrowLeft':
case 'ArrowRight': {
event.preventDefault();
break;
}
default:
//no op
break;
}
} else if (this.orientation === 'horizontal') {
switch (event.code) {
case 'ArrowRight': {
this.setTimer('scrollLeft', this.step);
event.preventDefault();
break;
}
case 'ArrowLeft': {
this.setTimer('scrollLeft', this.step * -1);
event.preventDefault();
break;
}
case 'ArrowDown':
case 'ArrowUp': {
event.preventDefault();
break;
}
default:
//no op
break;
}
}
},
onKeyUp() {
this.clearTimer();
},
repeat(bar, step) {
this.$refs.content[bar] += step;
this.moveBar();
},
setTimer(bar, step) {
this.clearTimer();
this.timer = setTimeout(() => {
this.repeat(bar, step);
}, 40);
},
clearTimer() {
if (this.timer) {
clearTimeout(this.timer);
}
},
2022-09-06 12:03:37 +00:00
onDocumentMouseMove(e) {
if (this.isXBarClicked) {
this.onMouseMoveForXBar(e);
2022-09-14 11:26:01 +00:00
} else if (this.isYBarClicked) {
2022-09-06 12:03:37 +00:00
this.onMouseMoveForYBar(e);
2022-09-14 11:26:01 +00:00
} else {
2022-09-06 12:03:37 +00:00
this.onMouseMoveForXBar(e);
this.onMouseMoveForYBar(e);
}
},
onMouseMoveForXBar(e) {
let deltaX = e.pageX - this.lastPageX;
2022-09-14 11:26:01 +00:00
2022-09-06 12:03:37 +00:00
this.lastPageX = e.pageX;
this.frame = this.requestAnimationFrame(() => {
this.$refs.content.scrollLeft += deltaX / this.scrollXRatio;
});
},
onMouseMoveForYBar(e) {
let deltaY = e.pageY - this.lastPageY;
2022-09-14 11:26:01 +00:00
2022-09-06 12:03:37 +00:00
this.lastPageY = e.pageY;
this.frame = this.requestAnimationFrame(() => {
this.$refs.content.scrollTop += deltaY / this.scrollYRatio;
});
},
2022-09-14 11:26:01 +00:00
onFocus(event) {
if (this.$refs.xBar.isSameNode(event.target)) {
this.orientation = 'horizontal';
} else if (this.$refs.yBar.isSameNode(event.target)) {
this.orientation = 'vertical';
}
},
onBlur() {
if (this.orientation === 'horizontal') {
this.orientation = 'vertical';
}
},
2022-09-06 12:03:37 +00:00
onDocumentMouseUp() {
this.$refs.yBar.setAttribute('data-p-scrollpanel-grabbed', 'false');
2023-05-24 14:08:05 +00:00
!this.isUnstyled && DomHandler.removeClass(this.$refs.yBar, 'p-scrollpanel-grabbed');
this.$refs.xBar.setAttribute('data-p-scrollpanel-grabbed', 'false');
2023-05-24 14:08:05 +00:00
!this.isUnstyled && DomHandler.removeClass(this.$refs.xBar, 'p-scrollpanel-grabbed');
document.body.setAttribute('data-p-scrollpanel-grabbed', 'false');
2023-05-24 14:08:05 +00:00
!this.isUnstyled && DomHandler.removeClass(document.body, 'p-scrollpanel-grabbed');
2022-09-06 12:03:37 +00:00
this.unbindDocumentMouseListeners();
this.isXBarClicked = false;
this.isYBarClicked = false;
},
requestAnimationFrame(f) {
let frame = window.requestAnimationFrame || this.timeoutFrame;
2022-09-14 11:26:01 +00:00
2022-09-06 12:03:37 +00:00
return frame(f);
},
refresh() {
this.moveBar();
},
scrollTop(scrollTop) {
let scrollableHeight = this.$refs.content.scrollHeight - this.$refs.content.clientHeight;
2022-09-14 11:26:01 +00:00
2022-09-06 12:03:37 +00:00
scrollTop = scrollTop > scrollableHeight ? scrollableHeight : scrollTop > 0 ? scrollTop : 0;
2022-09-14 11:26:01 +00:00
this.$refs.content.scrollTop = scrollTop;
},
timeoutFrame(fn) {
setTimeout(fn, 0);
2022-09-06 12:03:37 +00:00
},
bindDocumentMouseListeners() {
2022-09-14 11:26:01 +00:00
if (!this.documentMouseMoveListener) {
this.documentMouseMoveListener = (e) => {
this.onDocumentMouseMove(e);
};
2022-09-06 12:03:37 +00:00
2022-09-14 11:26:01 +00:00
document.addEventListener('mousemove', this.documentMouseMoveListener);
2022-09-06 12:03:37 +00:00
}
if (!this.documentMouseUpListener) {
2022-09-14 11:26:01 +00:00
this.documentMouseUpListener = (e) => {
this.onDocumentMouseUp(e);
};
document.addEventListener('mouseup', this.documentMouseUpListener);
}
},
unbindDocumentMouseListeners() {
if (this.documentMouseMoveListener) {
document.removeEventListener('mousemove', this.documentMouseMoveListener);
this.documentMouseMoveListener = null;
2022-09-06 12:03:37 +00:00
}
2022-09-14 11:26:01 +00:00
if (this.documentMouseUpListener) {
document.removeEventListener('mouseup', this.documentMouseUpListener);
this.documentMouseUpListener = null;
}
},
2022-09-06 12:03:37 +00:00
bindDocumentResizeListener() {
2022-09-14 11:26:01 +00:00
if (!this.documentResizeListener) {
this.documentResizeListener = () => {
this.moveBar();
};
window.addEventListener('resize', this.documentResizeListener);
}
},
unbindDocumentResizeListener() {
if (this.documentResizeListener) {
window.removeEventListener('resize', this.documentResizeListener);
this.documentResizeListener = null;
}
}
2023-12-07 06:49:59 +00:00
},
computed: {
contentId() {
2024-02-02 15:47:51 +00:00
return this.id + '_content';
2023-12-07 06:49:59 +00:00
}
2022-09-06 12:03:37 +00:00
}
2022-09-14 11:26:01 +00:00
};
2022-09-06 12:03:37 +00:00
</script>