用react启动。我发现这个codepen可以完美地在codepen上工作,如果我将其导出为香草html/css/javascript,它就可以在我的浏览器中工作。
然而,我想把它集成到一个React项目中,但是复制粘贴并没有起到作用,它呈现出很好的效果,只是不会像在coDepen中那样进行交互。
我的HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>delta-x</title>
<link rel="stylesheet" href="../src/stylesheets/styles.css" />
</head>
<body>
<div id="root">not rendered</div>
<script src="./bundle.js"></script>
</body>
</html>
我的组件(我只是简单地在codepen babel脚本上导入了React和ReactDOM模块)
import React from "react";
import ReactDOM from "react-dom";
function decimalRound(number, decimalNumbers) {
const roundFactor = 10 ** decimalNumbers;
return Math.round(number * roundFactor) / roundFactor;
}
class ResizableContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
totalWidth: 0,
percentualIncrement: 0,
resizing: false,
startMousePositionX: null,
resizerIndex: null,
};
this.containerRef = React.createRef();
}
// right now it gets called only on init, but if user add a dynamic prop it will be called on prop change and fail
static getDerivedStateFromProps(props, state) {
const children = React.Children.toArray(props.children);
const numberOfColumns = children.length;
const newChildren = [];
const resizersWidth = (numberOfColumns - 1) * props.resizerWidth;
const unsetWidth = children.reduce(
(acc, child) => acc - (child.props.initialWidth || 0),
1
);
const defaultInitialWidth =
unsetWidth / children.filter((child) => !child.props.initialWidth).length;
// 1) init columns
const columns = children.map((child) => ({
percentualWidth: child.props.initialWidth || defaultInitialWidth,
minWidth: child.props.minWidth,
maxWidth: child.props.maxWidth,
isResizing: false,
}));
// 2) insert a resizer between the columns, TODO: check if child is ResizableColumn
for (
let i = 0, columnIndex = 0, splitterIndex = 0;
i < numberOfColumns * 2 - 1;
i++
) {
if (i % 2 === 0) {
newChildren.push(
React.cloneElement(children[columnIndex], {
resizersWidth,
percentualWidth: columns[columnIndex].percentualWidth,
})
);
columnIndex++;
} else {
newChildren.push(<Resizer />);
splitterIndex++;
}
}
return {
processedChildren: newChildren,
columns,
resizersWidth,
};
}
componentDidMount() {
const container = this.containerRef.current;
const paddingRight = parseInt(
window.getComputedStyle(container).getPropertyValue("padding-right"),
10
);
const paddingLeft = parseInt(
window.getComputedStyle(container).getPropertyValue("padding-left"),
10
);
this.setState(
(prevState) => ({
totalWidth:
container.offsetWidth -
paddingLeft -
paddingRight -
prevState.resizersWidth,
}),
() => console.log("tw: ", this.state.totalWidth)
);
}
updateColumns(columns, resizerIndex) {
const leftColumnIndex = resizerIndex;
const rightColumnIndex = resizerIndex + 1;
const columnsPercWidthSum =
columns[leftColumnIndex].percentualWidth +
columns[rightColumnIndex].percentualWidth;
const {
maxWidth: leftColMaxWidth = columnsPercWidthSum,
minWidth: leftColMinWidth = 0,
} = columns[leftColumnIndex];
const {
maxWidth: rightColMaxWidth = columnsPercWidthSum,
minWidth: rightColMinWidth = 0,
} = columns[rightColumnIndex];
console.log("seh: ", {
columnsPercWidthSum,
leftColMaxWidth,
leftColMinWidth,
rightColMaxWidth,
rightColMinWidth,
});
return columns.map((column, index) => {
if (index === leftColumnIndex) {
return {
...column,
maxPercentualWidth: Math.min(
leftColMaxWidth,
columnsPercWidthSum - rightColMinWidth
),
minPercentualWidth: Math.max(
leftColMinWidth,
columnsPercWidthSum - rightColMaxWidth
),
isResizing: true,
resizeSide: 1, //left
};
} else if (index === rightColumnIndex) {
return {
...column,
isResizing: true,
maxPercentualWidth: Math.min(
rightColMaxWidth,
columnsPercWidthSum - leftColMinWidth
),
minPercentualWidth: Math.max(
rightColMinWidth,
columnsPercWidthSum - leftColMaxWidth
),
resizeSide: -1, //right
};
}
return column;
});
}
mouseDownHandler = (resizerIndex) => (event) => {
const mousePositionX = event.clientX;
// set which colums are being resized based on resizerIndex
this.setState(
(prevState) => ({
resizing: true,
startMousePositionX: mousePositionX,
resizerIndex,
columns: this.updateColumns(prevState.columns, resizerIndex),
}),
() => {
window.addEventListener("mousemove", this.resize, false);
window.addEventListener("mouseup", this.stopResize, false);
}
);
};
resize = (event) => {
const mousePositionX = event.clientX;
const increment = mousePositionX - this.state.startMousePositionX;
if (increment) {
this.setState((prevState) => ({
percentualIncrement: decimalRound(increment / prevState.totalWidth, 4),
}));
}
};
stopResize = (event) => {
console.log("stop");
this.setState((prevState) => ({
percentualIncrement: 0,
resizing: false,
resizerIndex: null,
startMousePositionX: null,
columns: prevState.columns.map((column) => {
return {
...column,
isResizing: false,
resizeSide: null,
percentualWidth: decimalRound(
this.calculatePercentualWidth(
column,
prevState.percentualIncrement
),
4
),
};
}),
}));
window.removeEventListener("mousemove", this.resize, false);
window.removeEventListener("mouseup", this.stopResize, false);
};
calculatePercentualWidth = (
{
isResizing,
percentualWidth,
resizeSide,
maxPercentualWidth,
minPercentualWidth,
},
percentualIncrement
) => {
const currPercentualWidth =
percentualWidth + (isResizing ? resizeSide * percentualIncrement : 0);
if (currPercentualWidth < minPercentualWidth) {
return minPercentualWidth;
} else if (currPercentualWidth > maxPercentualWidth) {
return maxPercentualWidth;
}
return currPercentualWidth;
};
renderChildren = (children) => {
let columnIndex = 0;
let splitterIndex = 0;
const { columns, percentualIncrement } = this.state;
return React.Children.map(children, (child) => {
let clonedChild;
if (child.type.displayName === "ResizableColumn") {
const column = columns[columnIndex];
clonedChild = React.cloneElement(child, {
percentualWidth: this.calculatePercentualWidth(
column,
percentualIncrement
),
});
columnIndex++;
} else if (child.type.displayName === "Resizer") {
clonedChild = React.cloneElement(child, {
mouseDownHandler: this.mouseDownHandler(splitterIndex),
isResizing: this.state.resizing,
});
splitterIndex++;
} else {
//throw error?
clonedChild = child;
}
return clonedChild;
});
};
render() {
console.log(this.state);
const style = this.state.resizing
? {
cursor: "col-resize",
userSelect: "none",
}
: {};
return (
<div className="container" style={style} ref={this.containerRef}>
{this.renderChildren(this.state.processedChildren)}
</div>
);
}
}
class ResizableColumn extends React.PureComponent {
static displayName = "ResizableColumn";
render() {
const s = {
margin: 0,
overflowX: "hidden",
width: `calc((100% - ${this.props.resizersWidth}px) * ${this.props.percentualWidth})`,
};
console.log("Col width: ", this.props.percentualWidth);
return (
<div style={s} ref={this.columnRef}>
{this.props.children}
</div>
);
}
}
class Resizer extends React.PureComponent {
static displayName = "Resizer";
render() {
const s = {
margin: 0,
};
return (
<div
style={s}
className="resizer"
onMouseDown={this.props.mouseDownHandler}
/>
);
}
}
class App extends React.Component {
render() {
return (
<ResizableContainer resizerWidth={15}>
<ResizableColumn minWidth={0.25} initialWidth={0.55}>
{" "}
1 Hello, world!
</ResizableColumn>
<ResizableColumn maxWidth={0.3}>2 Hello, world!</ResizableColumn>
<ResizableColumn>3 Hello, world!</ResizableColumn>
<ResizableColumn>4 Hello, world!</ResizableColumn>
</ResizableContainer>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
package.json
{
"name": "delta-x-frontend",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"dev": "webpack --mode=development",
"build": "webpack",
"start": "webpack serve --open",
"format": "prettier \"src/**/*.{html,scss,js}\" --write",
"lint": "eslint \"src/**/*.{js,jsx}\" --quiet",
"saas": "sass src/sass/:src/stylesheets"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.13.10",
"@babel/preset-env": "^7.13.10",
"@babel/preset-react": "^7.12.13",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"eslint": "^7.22.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-react": "^7.22.0",
"prettier": "^2.2.1",
"webpack": "^5.27.1",
"webpack-cli": "^4.5.0",
"webpack-dev-server": "^3.11.2"
},
"dependencies": {
"react": "^17.0.1",
"react-dom": "^17.0.1"
}
}
得到了它,所以我必须确保我使用的是相同的反应版本,因为在coDepen这是react@16.3.0
和react-dom@16.3.0
以及coDepen Babel插件,以确保我得到我的绒毛没错。