flowbite-react
Version:
Official React components built for Flowbite and Tailwind CSS
288 lines (285 loc) • 9.26 kB
JavaScript
import { jsx } from 'react/jsx-runtime';
import classnames from 'classnames';
import debounce from 'debounce';
import React, { PureComponent } from 'react';
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
const SCROLL_END_DEBOUNCE = 300;
const LEFT_BUTTON = 0;
class ScrollContainer extends PureComponent {
constructor(props) {
super(props);
__publicField(this, "container");
__publicField(this, "scrolling");
__publicField(this, "started");
__publicField(this, "pressed");
__publicField(this, "isMobile", false);
__publicField(this, "internal");
__publicField(this, "scrollLeft");
__publicField(this, "scrollTop");
__publicField(this, "clientX");
__publicField(this, "clientY");
// Simulate 'onEndScroll' event that fires when scrolling is stopped
__publicField(this, "onEndScroll", () => {
this.scrolling = false;
if (!this.pressed && this.started) {
this.processEnd();
}
});
__publicField(this, "onScroll", () => {
const container = this.container.current;
if (container.scrollLeft !== this.scrollLeft || container.scrollTop !== this.scrollTop) {
this.scrolling = true;
this.processScroll();
this.onEndScroll();
}
});
__publicField(this, "onTouchStart", (e) => {
const { nativeMobileScroll } = this.props;
if (this.isDraggable(e.target)) {
this.internal = true;
if (nativeMobileScroll && this.scrolling) {
this.pressed = true;
} else {
const touch = e.touches[0];
this.processClick(touch.clientX, touch.clientY);
if (!nativeMobileScroll && this.props.stopPropagation) {
e.stopPropagation();
}
}
}
});
__publicField(this, "onTouchEnd", () => {
const { nativeMobileScroll } = this.props;
if (this.pressed) {
if (this.started && (!this.scrolling || !nativeMobileScroll)) {
this.processEnd();
} else {
this.pressed = false;
}
this.forceUpdate();
}
});
__publicField(this, "onTouchMove", (e) => {
const { nativeMobileScroll } = this.props;
if (this.pressed && (!nativeMobileScroll || !this.isMobile)) {
const touch = e.touches[0];
if (touch) {
this.processMove(touch.clientX, touch.clientY);
}
e.preventDefault();
if (this.props.stopPropagation) {
e.stopPropagation();
}
}
});
__publicField(this, "onMouseDown", (e) => {
if (this.isDraggable(e.target) && this.isScrollable()) {
this.internal = true;
if (this.props?.buttons?.indexOf(e.button) !== -1) {
this.processClick(e.clientX, e.clientY);
e.preventDefault();
if (this.props.stopPropagation) {
e.stopPropagation();
}
}
}
});
__publicField(this, "onMouseMove", (e) => {
if (this.pressed) {
this.processMove(e.clientX, e.clientY);
e.preventDefault();
if (this.props.stopPropagation) {
e.stopPropagation();
}
}
});
__publicField(this, "onMouseUp", (e) => {
if (this.pressed) {
if (this.started) {
this.processEnd();
} else {
this.internal = false;
this.pressed = false;
this.forceUpdate();
if (this.props.onClick) {
this.props.onClick(e);
}
}
e.preventDefault();
if (this.props.stopPropagation) {
e.stopPropagation();
}
}
});
this.container = React.createRef();
this.onEndScroll = debounce(this.onEndScroll, SCROLL_END_DEBOUNCE);
this.scrolling = false;
this.started = false;
this.pressed = false;
this.internal = false;
this.getRef = this.getRef.bind(this);
}
componentDidMount() {
const { nativeMobileScroll } = this.props;
const container = this.container.current;
window.addEventListener("mouseup", this.onMouseUp);
window.addEventListener("mousemove", this.onMouseMove);
window.addEventListener("touchmove", this.onTouchMove, { passive: false });
window.addEventListener("touchend", this.onTouchEnd);
container.addEventListener("touchstart", this.onTouchStart, {
passive: false
});
container.addEventListener("mousedown", this.onMouseDown, {
passive: false
});
if (nativeMobileScroll) {
this.isMobile = this.isMobileDevice();
if (this.isMobile) {
this.forceUpdate();
}
}
}
componentWillUnmount() {
window.removeEventListener("mouseup", this.onMouseUp);
window.removeEventListener("mousemove", this.onMouseMove);
window.removeEventListener("touchmove", this.onTouchMove);
window.removeEventListener("touchend", this.onTouchEnd);
}
getElement() {
return this.container.current;
}
isMobileDevice() {
return typeof window.orientation !== "undefined" || navigator.userAgent.indexOf("IEMobile") !== -1;
}
isDraggable(target) {
const ignoreElements = this.props.ignoreElements;
if (ignoreElements) {
const closest = target.closest(ignoreElements);
return closest === null || closest.contains(this.getElement());
} else {
return true;
}
}
isScrollable() {
const container = this.container.current;
return container && (container.scrollWidth > container.clientWidth || container.scrollHeight > container.clientHeight);
}
processClick(clientX, clientY) {
const container = this.container.current;
this.scrollLeft = container?.scrollLeft;
this.scrollTop = container?.scrollTop;
this.clientX = clientX;
this.clientY = clientY;
this.pressed = true;
}
processStart(changeCursor = true) {
const { onStartScroll } = this.props;
this.started = true;
if (changeCursor) {
document.body.classList.add("cursor-grab");
}
if (onStartScroll) {
onStartScroll({
external: !this.internal
});
}
this.forceUpdate();
}
// Process native scroll (scrollbar, mobile scroll)
processScroll() {
if (this.started) {
const { onScroll } = this.props;
if (onScroll) {
onScroll({
external: !this.internal
});
}
} else {
this.processStart(false);
}
}
// Process non-native scroll
processMove(newClientX, newClientY) {
const { horizontal, vertical, activationDistance, onScroll } = this.props;
const container = this.container.current;
if (!this.started) {
if (horizontal && Math.abs(newClientX - this.clientX) > activationDistance || vertical && Math.abs(newClientY - this.clientY) > activationDistance) {
this.clientX = newClientX;
this.clientY = newClientY;
this.processStart();
}
} else {
if (horizontal) {
container.scrollLeft -= newClientX - this.clientX;
}
if (vertical) {
container.scrollTop -= newClientY - this.clientY;
}
if (onScroll) {
onScroll({ external: !this.internal });
}
this.clientX = newClientX;
this.clientY = newClientY;
this.scrollLeft = container.scrollLeft;
this.scrollTop = container.scrollTop;
}
}
processEnd() {
const { onEndScroll } = this.props;
const container = this.container.current;
if (container && onEndScroll) {
onEndScroll({
external: !this.internal
});
}
this.pressed = false;
this.started = false;
this.scrolling = false;
this.internal = false;
document.body.classList.remove("cursor-grab");
this.forceUpdate();
}
getRef(el) {
[this.container, this.props.innerRef].forEach((ref) => {
if (ref) {
if (typeof ref === "function") {
ref(el);
} else {
ref.current = el;
}
}
});
}
render() {
const { children, draggingClassName, className, style, hideScrollbars } = this.props;
return /* @__PURE__ */ jsx(
"div",
{
className: classnames(className, this.pressed && draggingClassName, {
"!scroll-auto [&>*]:pointer-events-none [&>*]:cursor-grab": this.pressed,
"overflow-auto": this.isMobile,
"overflow-hidden !overflow-x-hidden [scrollbar-width:none]": hideScrollbars,
"[&::-webkit-scrollbar]:[-webkit-appearance:none !important] [&::-webkit-scrollbar]:!hidden [&::-webkit-scrollbar]:!h-0 [&::-webkit-scrollbar]:!w-0 [&::-webkit-scrollbar]:!bg-transparent": hideScrollbars
}),
style,
ref: this.getRef,
onScroll: this.onScroll,
children
}
);
}
}
__publicField(ScrollContainer, "defaultProps", {
nativeMobileScroll: true,
hideScrollbars: true,
activationDistance: 10,
vertical: true,
horizontal: true,
stopPropagation: false,
style: {},
buttons: [LEFT_BUTTON]
});
export { ScrollContainer as default };
//# sourceMappingURL=drag-scroll.js.map