Commit 59755fa9 authored by ChrisH's avatar ChrisH Committed by GitHub

feat: Added children type declaration for React 18 (#36)

* Update PullToRefresh.tsx

- React 18 requires children props type to be explicitly declared

* updated react version
parent d4f43f1d
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -32,8 +32,8 @@ ...@@ -32,8 +32,8 @@
"awesome-typescript-loader": "^5.2.1", "awesome-typescript-loader": "^5.2.1",
"jest": "^23.6.0", "jest": "^23.6.0",
"path": "^0.12.7", "path": "^0.12.7",
"react": "^16.5.2", "react": "^18.0.0",
"react-dom": "^16.5.2", "react-dom": "^18.0.0",
"react-hot-loader": "^4.3.11", "react-hot-loader": "^4.3.11",
"react-test-renderer": "^16.5.2", "react-test-renderer": "^16.5.2",
"rimraf": "^2.6.2", "rimraf": "^2.6.2",
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
"webpack-dev-server": "^3.1.9" "webpack-dev-server": "^3.1.9"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^16.5.2", "react": "^18.0.0",
"react-dom": "^16.5.2" "react-dom": "^18.0.0"
} }
} }
...@@ -2,230 +2,253 @@ import * as React from "react"; ...@@ -2,230 +2,253 @@ import * as React from "react";
import { DIRECTION, isTreeScrollable } from "../isScrollable"; import { DIRECTION, isTreeScrollable } from "../isScrollable";
export interface PullToRefreshProps { export interface PullToRefreshProps {
pullDownContent: JSX.Element; pullDownContent: JSX.Element;
releaseContent: JSX.Element; releaseContent: JSX.Element;
refreshContent: JSX.Element; refreshContent: JSX.Element;
pullDownThreshold: number; pullDownThreshold: number;
onRefresh: () => Promise<any>; onRefresh: () => Promise<any>;
triggerHeight?: number | "auto"; triggerHeight?: number | "auto";
backgroundColor?: string; backgroundColor?: string;
containerStyle?: React.CSSProperties; containerStyle?: React.CSSProperties;
startInvisible?: boolean; startInvisible?: boolean;
children?: React.ReactNode;
} }
export interface PullToRefreshState { export interface PullToRefreshState {
pullToRefreshThresholdBreached: boolean; pullToRefreshThresholdBreached: boolean;
maxPullDownDistance: number; maxPullDownDistance: number;
onRefreshing: boolean; onRefreshing: boolean;
} }
export class PullToRefresh extends React.Component<PullToRefreshProps, PullToRefreshState> { export class PullToRefresh extends React.Component<
private container: any; PullToRefreshProps,
PullToRefreshState
private containerRef(container) { > {
this.container = container; private container: any;
private containerRef(container) {
this.container = container;
}
private pullDown: any;
private pullDownRef(pullDown) {
this.pullDown = pullDown;
const maxPullDownDistance =
this.pullDown &&
this.pullDown.firstChild &&
this.pullDown.firstChild["getBoundingClientRect"]
? this.pullDown.firstChild["getBoundingClientRect"]().height
: 0;
this.setState({ maxPullDownDistance });
}
private dragging = false;
private startY = 0;
private currentY = 0;
constructor(props: Readonly<PullToRefreshProps>) {
super(props);
this.state = {
pullToRefreshThresholdBreached: false,
maxPullDownDistance: 0,
onRefreshing: false,
};
this.containerRef = this.containerRef.bind(this);
this.pullDownRef = this.pullDownRef.bind(this);
this.onTouchStart = this.onTouchStart.bind(this);
this.onTouchMove = this.onTouchMove.bind(this);
this.onEnd = this.onEnd.bind(this);
}
public componentDidMount(): void {
if (!this.container) {
return;
} }
private pullDown: any; this.container.addEventListener("touchstart", this.onTouchStart);
this.container.addEventListener("touchmove", this.onTouchMove);
private pullDownRef(pullDown) { this.container.addEventListener("touchend", this.onEnd);
this.pullDown = pullDown; this.container.addEventListener("mousedown", this.onTouchStart);
const maxPullDownDistance = this.pullDown && this.pullDown.firstChild && this.pullDown.firstChild["getBoundingClientRect"] this.container.addEventListener("mousemove", this.onTouchMove);
? this.pullDown.firstChild["getBoundingClientRect"]().height : 0; this.container.addEventListener("mouseup", this.onEnd);
this.setState({maxPullDownDistance}); }
public componentWillUnmount(): void {
if (!this.container) {
return;
} }
private dragging = false; this.container.removeEventListener("touchstart", this.onTouchStart);
private startY = 0; this.container.removeEventListener("touchmove", this.onTouchMove);
private currentY = 0; this.container.removeEventListener("touchend", this.onEnd);
this.container.removeEventListener("mousedown", this.onTouchStart);
constructor(props: Readonly<PullToRefreshProps>) { this.container.removeEventListener("mousemove", this.onTouchMove);
super(props); this.container.removeEventListener("mouseup", this.onEnd);
this.state = { }
pullToRefreshThresholdBreached: false,
maxPullDownDistance: 0, private onTouchStart(e) {
onRefreshing: false, const { triggerHeight = 40 } = this.props;
}; this.startY = e["pageY"] || e.touches[0].pageY;
this.currentY = this.startY;
this.containerRef = this.containerRef.bind(this);
this.pullDownRef = this.pullDownRef.bind(this); if (triggerHeight === "auto") {
this.onTouchStart = this.onTouchStart.bind(this); const target = e.target;
this.onTouchMove = this.onTouchMove.bind(this);
this.onEnd = this.onEnd.bind(this); const container = this.container;
if (!container) {
return;
}
// an element we're touching can be scrolled up, so gesture is going to be a scroll gesture
if (e.type === "touchstart" && isTreeScrollable(target, DIRECTION.up)) {
return;
}
// even though we're not scrolling, the pull-to-refresh isn't visible to the user so cancel
if (container.getBoundingClientRect().top < 0) {
return;
}
} else {
const top =
this.container.getBoundingClientRect().top ||
this.container.getBoundingClientRect().y ||
0;
if (this.startY - top > triggerHeight) {
return;
}
} }
public componentDidMount(): void { this.dragging = true;
if (!this.container) { this.container.style.transition = "transform 0.2s cubic-bezier(0,0,0.31,1)";
return; this.pullDown.style.transition = "transform 0.2s cubic-bezier(0,0,0.31,1)";
} }
this.container.addEventListener("touchstart", this.onTouchStart);
this.container.addEventListener("touchmove", this.onTouchMove);
this.container.addEventListener("touchend", this.onEnd);
this.container.addEventListener("mousedown", this.onTouchStart);
this.container.addEventListener("mousemove", this.onTouchMove);
this.container.addEventListener("mouseup", this.onEnd);
}
public componentWillUnmount(): void { private onTouchMove(e) {
if (!this.container) { if (!this.dragging) {
return; return;
}
this.container.removeEventListener("touchstart", this.onTouchStart);
this.container.removeEventListener("touchmove", this.onTouchMove);
this.container.removeEventListener("touchend", this.onEnd);
this.container.removeEventListener("mousedown", this.onTouchStart);
this.container.removeEventListener("mousemove", this.onTouchMove);
this.container.removeEventListener("mouseup", this.onEnd);
} }
private onTouchStart(e) { this.currentY = e["pageY"] || e.touches[0].pageY;
const {triggerHeight = 40} = this.props; if (this.currentY < this.startY) {
this.startY = e["pageY"] || e.touches[0].pageY; return;
this.currentY = this.startY;
if (triggerHeight === "auto") {
const target = e.target;
const container = this.container;
if (!container) {
return;
}
// an element we're touching can be scrolled up, so gesture is going to be a scroll gesture
if (e.type === "touchstart" && isTreeScrollable(target, DIRECTION.up)) {
return;
}
// even though we're not scrolling, the pull-to-refresh isn't visible to the user so cancel
if (container.getBoundingClientRect().top < 0) {
return;
}
} else {
const top = this.container.getBoundingClientRect().top || this.container.getBoundingClientRect().y || 0;
if (this.startY - top > triggerHeight) {
return;
}
}
this.dragging = true;
this.container.style.transition = "transform 0.2s cubic-bezier(0,0,0.31,1)";
this.pullDown.style.transition = "transform 0.2s cubic-bezier(0,0,0.31,1)";
} }
private onTouchMove(e) { if (e.cancelable) {
if (!this.dragging) { e.preventDefault();
return; }
}
this.currentY = e["pageY"] || e.touches[0].pageY;
if (this.currentY < this.startY) {
return;
}
if (e.cancelable) {
e.preventDefault();
}
if ((this.currentY - this.startY) >= this.props.pullDownThreshold) {
this.setState({
pullToRefreshThresholdBreached: true,
});
}
if (this.currentY - this.startY > this.state.maxPullDownDistance) { if (this.currentY - this.startY >= this.props.pullDownThreshold) {
return; this.setState({
} pullToRefreshThresholdBreached: true,
});
}
this.container.style.overflow = "visible"; if (this.currentY - this.startY > this.state.maxPullDownDistance) {
this.container.style.transform = `translate(0px, ${this.currentY - this.startY}px)`; return;
this.pullDown.style.visibility = "visible";
} }
private onEnd() { this.container.style.overflow = "visible";
this.dragging = false; this.container.style.transform = `translate(0px, ${
this.startY = 0; this.currentY - this.startY
this.currentY = 0; }px)`;
this.pullDown.style.visibility = "visible";
if (!this.state.pullToRefreshThresholdBreached) { }
this.pullDown.style.visibility = this.props.startInvisible ? "hidden" : "visible";
this.initContainer(); private onEnd() {
return; this.dragging = false;
} this.startY = 0;
this.currentY = 0;
this.container.style.overflow = "visible";
this.container.style.transform = `translate(0px, ${this.props.pullDownThreshold}px)`; if (!this.state.pullToRefreshThresholdBreached) {
this.setState({ this.pullDown.style.visibility = this.props.startInvisible
onRefreshing: true, ? "hidden"
}, () => { : "visible";
this.props.onRefresh().then(() => { this.initContainer();
this.initContainer(); return;
setTimeout(() => {
this.setState({
onRefreshing: false,
pullToRefreshThresholdBreached: false,
});
}, 200);
});
});
} }
private initContainer() { this.container.style.overflow = "visible";
requestAnimationFrame(() => { this.container.style.transform = `translate(0px, ${this.props.pullDownThreshold}px)`;
if (this.container) { this.setState(
this.container.style.overflow = "auto"; {
this.container.style.transform = "none"; onRefreshing: true,
} },
() => {
this.props.onRefresh().then(() => {
this.initContainer();
setTimeout(() => {
this.setState({
onRefreshing: false,
pullToRefreshThresholdBreached: false,
});
}, 200);
}); });
},
);
}
private initContainer() {
requestAnimationFrame(() => {
if (this.container) {
this.container.style.overflow = "auto";
this.container.style.transform = "none";
}
});
}
private renderPullDownContent() {
const { releaseContent, pullDownContent, refreshContent, startInvisible } =
this.props;
const { onRefreshing, pullToRefreshThresholdBreached } = this.state;
const content = onRefreshing
? refreshContent
: pullToRefreshThresholdBreached
? releaseContent
: pullDownContent;
const contentStyle: React.CSSProperties = {
position: "absolute",
overflow: "hidden",
left: 0,
right: 0,
top: 0,
visibility: startInvisible ? "hidden" : "visible",
};
return (
<div id="ptr-pull-down" style={contentStyle} ref={this.pullDownRef}>
{content}
</div>
);
}
public render() {
const { backgroundColor } = this.props;
const containerStyle: React.CSSProperties = {
height: "auto",
overflow: "hidden",
WebkitOverflowScrolling: "touch",
position: "relative",
zIndex: 1,
};
if (this.props.containerStyle) {
Object.keys(this.props.containerStyle).forEach((key: string) => {
containerStyle[key] = this.props.containerStyle[key];
});
} }
private renderPullDownContent() { if (backgroundColor) {
const {releaseContent, pullDownContent, refreshContent, startInvisible} = this.props; containerStyle.backgroundColor = backgroundColor;
const {onRefreshing, pullToRefreshThresholdBreached} = this.state;
const content = onRefreshing ? refreshContent : pullToRefreshThresholdBreached ? releaseContent : pullDownContent;
const contentStyle: React.CSSProperties = {
position: "absolute",
overflow: "hidden",
left: 0,
right: 0,
top: 0,
visibility: startInvisible ? "hidden" : "visible",
};
return (
<div id="ptr-pull-down" style={contentStyle} ref={this.pullDownRef}>
{content}
</div>
);
} }
public render() { return (
const {backgroundColor} = this.props; <div id="ptr-parent" style={containerStyle}>
const containerStyle: React.CSSProperties = { {this.renderPullDownContent()}
height: "auto", <div id="ptr-container" ref={this.containerRef} style={containerStyle}>
overflow: "hidden", {this.props.children}
WebkitOverflowScrolling: "touch", </div>
position: "relative", </div>
zIndex: 1, );
}; }
if (this.props.containerStyle) {
Object.keys(this.props.containerStyle).forEach((key: string) => {
containerStyle[key] = this.props.containerStyle[key];
});
}
if (backgroundColor) {
containerStyle.backgroundColor = backgroundColor;
}
return (
<div id="ptr-parent" style={containerStyle}>
{this.renderPullDownContent()}
<div id="ptr-container" ref={this.containerRef} style={containerStyle}>
{this.props.children}
</div>
</div>
);
}
} }
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment