分享页面 / 分页

This commit is contained in:
HFO4 2020-02-13 11:53:46 +08:00
parent 100598d9a6
commit baafc12dbe
8 changed files with 547 additions and 101 deletions

View File

@ -5,7 +5,7 @@
"dependencies": {
"@material-ui/core": "^4.9.0",
"@material-ui/icons": "^4.5.1",
"@material-ui/lab": "^4.0.0-alpha.33",
"@material-ui/lab": "^4.0.0-alpha.42",
"axios": "^0.19.0",
"classnames": "^2.2.6",
"for-editor": "^0.3.5",

View File

@ -13,16 +13,17 @@ import {
import Auth from "./middleware/Auth";
import { CssBaseline, makeStyles, ThemeProvider } from "@material-ui/core";
import PageLoading from "./component/Placeholder/PageLoading.js"
import TextViewer from "./component/Viewer/Text";
import DocViewer from "./component/Viewer/Doc";
import SharePreload from "./component/Share/SharePreload";
import Download from "./component/Download/Download";
import MyShare from "./component/MyShare";
// Lazy loads
const LoginForm = React.lazy(() => import("./component/Login/LoginForm"));
const FileManager = React.lazy(() => import ("./component/FileManager/FileManager.js" ));
const VideoPreview = React.lazy(() => import ("./component/Viewer/Video.js" ));
const SearchResult = React.lazy(() => import ("./component/Share/SearchResult" ));
const MyShare = React.lazy(() => import ("./component/Share/MyShare"));
const Download = React.lazy(() => import ("./component/Download/Download"));
const SharePreload = React.lazy(() => import ("./component/Share/SharePreload"));
const DocViewer = React.lazy(() => import ("./component/Viewer/Doc"));
const TextViewer = React.lazy(() => import ("./component/Viewer/Text"));
export default function App() {
const themeConfig = useSelector(state => state.siteConfig.theme);
@ -107,6 +108,12 @@ export default function App() {
</Suspense>
</AuthRoute>
<AuthRoute path={`${path}search`} isLogin={isLogin}>
<Suspense fallback={<PageLoading/>}>
<SearchResult />
</Suspense>
</AuthRoute>
<Route path={`${path}login`}>
<Suspense fallback={<PageLoading/>}>
<LoginForm />

View File

@ -51,10 +51,15 @@ import Auth from "../../middleware/Auth";
import { Archive, Unarchive } from "@material-ui/icons";
import { openCompressDialog } from "../../actions";
import Menu from "@material-ui/core/Menu";
import {FolderUpload} from "mdi-material-ui";
const styles = theme => ({
propover: {
minWidth: "200px!important"
},
divider:{
marginTop:4,
marginBottom:4,
}
});
@ -176,9 +181,9 @@ class ContextMenuCompoment extends Component {
);
};
clickUpload = () => {
clickUpload = id => {
this.props.changeContextMenu("empty", false);
let uploadButton = document.getElementsByClassName("uploadFileForm")[0];
let uploadButton = document.getElementsByClassName(id)[0];
if (document.body.contains(uploadButton)) {
uploadButton.click();
} else {
@ -296,7 +301,7 @@ class ContextMenuCompoment extends Component {
>
{this.props.menuType === "empty" && (
<>
<MenuItem onClick={this.clickUpload}>
<MenuItem onClick={()=>this.clickUpload("uploadFileForm")}>
<ListItemIcon>
<UploadIcon />
</ListItemIcon>
@ -304,6 +309,14 @@ class ContextMenuCompoment extends Component {
上传文件
</Typography>
</MenuItem>
<MenuItem onClick={()=>this.clickUpload("uploadFolderForm")}>
<ListItemIcon>
<FolderUpload />
</ListItemIcon>
<Typography variant="inherit">
上传目录
</Typography>
</MenuItem>
{user.group.allowRemoteDownload && (
<MenuItem
onClick={() =>
@ -319,7 +332,7 @@ class ContextMenuCompoment extends Component {
</MenuItem>
)}
<Divider />
<Divider className={classes.divider}/>
<MenuItem
onClick={() =>
this.props.openCreateFolderDialog()
@ -346,7 +359,7 @@ class ContextMenuCompoment extends Component {
进入
</Typography>
</MenuItem>
{isHomePage && <Divider />}
{isHomePage && <Divider className={classes.divider} />}
</>
)}
{!this.props.isMultiple &&
@ -380,7 +393,7 @@ class ContextMenuCompoment extends Component {
下载
</Typography>
</MenuItem>
{isHomePage && <Divider />}
{isHomePage && <Divider className={classes.divider} />}
</>
)}
@ -466,7 +479,7 @@ class ContextMenuCompoment extends Component {
<Archive />
</ListItemIcon>
<Typography variant="inherit">
压缩
创建压缩文件
</Typography>
</MenuItem>
)}
@ -479,7 +492,7 @@ class ContextMenuCompoment extends Component {
<ShareIcon />
</ListItemIcon>
<Typography variant="inherit">
分享
创建分享链接
</Typography>
</MenuItem>
)}
@ -532,7 +545,7 @@ class ContextMenuCompoment extends Component {
</MenuItem>
}
<Divider />
<Divider className={classes.divider} />
<MenuItem
className={classes.propover}
onClick={() =>

View File

@ -14,6 +14,7 @@ import {
TextField,
Avatar
} from "@material-ui/core";
import {useLocation, withRouter} from "react-router";
const styles = theme => ({
card: {
@ -53,9 +54,14 @@ const mapDispatchToProps = dispatch => {
};
class LockedFileCompoment extends Component {
state = {
pwd: ""
};
constructor(props) {
super(props);
let query = new URLSearchParams(this.props.location.search);
this.state = {
pwd: query.get("password"),
};
}
handleChange = name => event => {
this.setState({ [name]: event.target.value });
@ -129,6 +135,6 @@ class LockedFileCompoment extends Component {
const LockedFile = connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(LockedFileCompoment));
)(withStyles(styles)(withRouter(LockedFileCompoment)));
export default LockedFile;

View File

@ -1,9 +1,9 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import { toggleSnackbar } from "../actions/index";
import { toggleSnackbar } from "../../actions";
import axios from "axios";
import OpenIcon from "@material-ui/icons/OpenInNew";
import FileIcon from "@material-ui/icons/InsertDriveFile";
import Pagination from "@material-ui/lab/Pagination";
import FolderIcon from "@material-ui/icons/Folder";
import LockIcon from "@material-ui/icons/Lock";
import UnlockIcon from "@material-ui/icons/LockOpen";
@ -27,10 +27,16 @@ import {
Button,
TextField
} from "@material-ui/core";
import API from "../middleware/Api";
import TypeIcon from "./FileManager/TypeIcon";
import API from "../../middleware/Api";
import TypeIcon from "../FileManager/TypeIcon";
import Chip from "@material-ui/core/Chip";
import Divider from "@material-ui/core/Divider";
import { VisibilityOff, VpnKey } from "@material-ui/icons";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import FormHelperText from "@material-ui/core/FormHelperText";
import FormControl from "@material-ui/core/FormControl";
import {withRouter} from "react-router-dom";
const styles = theme => ({
cardContainer: {
@ -67,13 +73,17 @@ const styles = theme => ({
marginTop: "30px"
},
loadMore: {
textAlign: "center",
textAlign: "right",
marginTop: "20px",
marginBottom: "20px"
marginBottom: "40px"
},
badge: {
marginLeft: theme.spacing(1),
height: 17,
height: 17
},
orderSelect:{
textAlign:"right",
marginTop: 5,
}
});
const mapStateToProps = state => {
@ -91,16 +101,14 @@ const mapDispatchToProps = dispatch => {
class MyShareCompoment extends Component {
state = {
page: 1,
total: 0,
shareList: [],
showPwd: null
showPwd: null,
orderBy:"created_at DESC",
};
componentDidMount = () => {
this.loadList(1);
};
loadMore = () => {
this.loadList(this.state.page + 1);
this.loadList(1,this.state.orderBy);
};
showPwd = pwd => {
@ -111,19 +119,17 @@ class MyShareCompoment extends Component {
this.setState({ showPwd: null });
};
removeShare = id => {
axios
.post("/Share/Delete", {
id: id
})
removeShare = (id) => {
API
.delete("/share/"+id)
.then(response => {
if (response.data.error !== 1) {
let oldList = this.state.shareList;
oldList = oldList.filter(value => {
return value.share_key !== id;
return value.key !== id;
});
this.setState({
shareList: oldList
shareList: oldList,
total:this.state.total-1,
});
this.props.toggleSnackbar(
"top",
@ -131,41 +137,65 @@ class MyShareCompoment extends Component {
"分享已取消",
"success"
);
}
if (oldList.length === 0){
this.loadList(1,this.state.orderBy);
}
})
.catch(error => {
this.props.toggleSnackbar("top", "right", "请求失败", "error");
this.props.toggleSnackbar("top", "right", error.message, "error");
});
};
changePermission = id => {
axios
.post("/Share/ChangePromission", {
id: id
let newPwd = Math.random()
.toString(36)
.substr(2)
.slice(2, 8);
let oldList = this.state.shareList;
let shareIndex = oldList.findIndex(value => {
return value.key === id;
});
API
.patch("/share/"+id, {
prop:"password",
value:oldList[shareIndex].password === "" ? newPwd : "",
})
.then(response => {
if (response.data.error !== 1) {
let oldList = this.state.shareList;
let shareIndex = oldList.findIndex(value => {
return value.share_key === id;
});
oldList[shareIndex].type =
oldList[shareIndex].type === "public"
? "private"
: "public";
oldList[shareIndex].share_pwd = response.data.newPwd;
this.setState({
shareList: oldList
});
}
oldList[shareIndex].password = response.data;
this.setState({
shareList: oldList
});
})
.catch(error => {
this.props.toggleSnackbar("top", "right", "请求失败", "error");
this.props.toggleSnackbar("top", "right", error.message, "error");
});
};
loadList = page => {
API.get("/share?page=" + this.state.page)
changePreviewOption = id => {
let oldList = this.state.shareList;
let shareIndex = oldList.findIndex(value => {
return value.key === id;
});
API
.patch("/share/"+id, {
prop:"preview_enabled",
value:oldList[shareIndex].preview?"false":"true",
})
.then(response => {
oldList[shareIndex].preview = response.data;
this.setState({
shareList: oldList
});
})
.catch(error => {
this.props.toggleSnackbar("top", "right", error.message, "error");
});
};
loadList = (page,orderBy) => {
let order = orderBy.split(" ");
API.get("/share?page=" + page + "&order_by=" + order[0] + "&order=" + order[1])
.then(response => {
if (response.data.items.length === 0) {
this.props.toggleSnackbar(
@ -176,7 +206,7 @@ class MyShareCompoment extends Component {
);
}
this.setState({
page: page,
total: response.data.total,
shareList: response.data.items
});
})
@ -185,8 +215,22 @@ class MyShareCompoment extends Component {
});
};
handlePageChange = (event, value) => {
this.setState({
page: value
});
this.loadList(value,this.state.orderBy);
};
handleOrderChange = event => {
this.setState({
orderBy:event.target.value,
});
this.loadList(this.state.page,event.target.value);
};
isExpired = share => {
return share.expire === 0 || share.remain_downloads === 0;
return share.expire < -1 || share.remain_downloads === 0;
};
render() {
@ -194,10 +238,26 @@ class MyShareCompoment extends Component {
return (
<div className={classes.layout}>
<Typography color="textSecondary" variant="h3">
{" "}
我的分享{" "}
</Typography>{" "}
<Grid container>
<Grid sm={6} xs={6}>
<Typography color="textSecondary" variant="h4">
我的分享
</Typography>
</Grid>
<Grid sm={6} xs={6} className={classes.orderSelect}>
<FormControl>
<Select color={"secondary"} onChange={this.handleOrderChange} value={this.state.orderBy}>
<MenuItem value={"created_at DESC"}>创建日期由晚到早</MenuItem>
<MenuItem value={"created_at ASC"}>创建日期由早到晚</MenuItem>
<MenuItem value={"downloads DESC"}>下载次数由大到小</MenuItem>
<MenuItem value={"downloads ASC"}>下载次数由小到大</MenuItem>
<MenuItem value={"views DESC"}>浏览次数由大到小</MenuItem>
<MenuItem value={"views ASC"}>浏览次数由小到大</MenuItem>
</Select>
</FormControl>
</Grid>
</Grid>
<Grid container spacing={24} className={classes.gird}>
{this.state.shareList.map(value => (
<Grid
@ -209,7 +269,6 @@ class MyShareCompoment extends Component {
>
<Card className={classes.card}>
<CardHeader
avatar={
<div>
{!value.is_dir && (
@ -265,15 +324,18 @@ class MyShareCompoment extends Component {
</span>
}
/>
<Divider/>
<Divider />
<CardActions
disableActionSpacing
style={{ display:"block",textAlign: "right" }}
style={{
display: "block",
textAlign: "right"
}}
>
<Tooltip placement="top" title="打开">
<IconButton
onClick={() =>
window.open("/s/" + value.key)
this.props.history.push("/s/"+value.key + (value.password === "" ?"":"?password=" + value.password))
}
>
<OpenIcon />
@ -299,23 +361,23 @@ class MyShareCompoment extends Component {
title="查看密码"
onClick={() =>
this.showPwd(
value.share_pwd
value.password
)
}
>
<IconButton>
<EyeIcon />
<VpnKey />
</IconButton>
</Tooltip>
</>
)}{" "}
)}
{value.password === "" && (
<Tooltip
placement="top"
title="变更为私密分享"
onClick={() =>
this.changePermission(
value.share_key
value.key
)
}
>
@ -324,6 +386,27 @@ class MyShareCompoment extends Component {
</IconButton>
</Tooltip>
)}
<Tooltip
placement="top"
title={
value.preview
? "禁止预览"
: "允许预览"
}
onClick={() =>
this.changePreviewOption(
value.key
)
}
>
<IconButton>
{!value.preview ? (
<VisibilityOff />
) : (
<EyeIcon />
)}
</IconButton>
</Tooltip>
<Tooltip
placement="top"
title="取消分享"
@ -341,14 +424,11 @@ class MyShareCompoment extends Component {
))}
</Grid>
<div className={classes.loadMore}>
<Button
size="large"
className={classes.margin}
disabled={this.state.shareList.length < 10}
onClick={this.loadMore}
>
继续加载{" "}
</Button>{" "}
<Pagination
count={Math.ceil(this.state.total / 18)}
onChange={this.handlePageChange}
color="secondary"
/>
</div>{" "}
<Dialog
open={this.state.showPwd !== null}
@ -377,6 +457,6 @@ class MyShareCompoment extends Component {
const MyShare = connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(MyShareCompoment));
)(withStyles(styles)(withRouter(MyShareCompoment)));
export default MyShare;

View File

@ -0,0 +1,348 @@
import React, { Component } from "react";
import { connect } from "react-redux";
import { toggleSnackbar } from "../../actions";
import axios from "axios";
import OpenIcon from "@material-ui/icons/OpenInNew";
import Pagination from "@material-ui/lab/Pagination";
import FolderIcon from "@material-ui/icons/Folder";
import LockIcon from "@material-ui/icons/Lock";
import UnlockIcon from "@material-ui/icons/LockOpen";
import EyeIcon from "@material-ui/icons/RemoveRedEye";
import DeleteIcon from "@material-ui/icons/Delete";
import {
withStyles,
Tooltip,
Card,
Avatar,
CardHeader,
CardActions,
Typography,
Grid,
IconButton,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Button,
TextField
} from "@material-ui/core";
import API from "../../middleware/Api";
import TypeIcon from "../FileManager/TypeIcon";
import Chip from "@material-ui/core/Chip";
import Divider from "@material-ui/core/Divider";
import { VisibilityOff, VpnKey } from "@material-ui/icons";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import FormHelperText from "@material-ui/core/FormHelperText";
import FormControl from "@material-ui/core/FormControl";
import {withRouter} from "react-router-dom";
const styles = theme => ({
cardContainer: {
padding: theme.spacing(1)
},
card: {
maxWidth: 400,
margin: "0 auto"
},
actions: {
display: "flex"
},
layout: {
width: "auto",
marginTop: "50px",
marginLeft: theme.spacing(3),
marginRight: theme.spacing(3),
[theme.breakpoints.up(1100 + theme.spacing(3) * 2)]: {
width: 1100,
marginLeft: "auto",
marginRight: "auto"
}
},
shareTitle: {
maxWidth: "200px"
},
avatarFile: {
backgroundColor: theme.palette.primary.light
},
avatarFolder: {
backgroundColor: theme.palette.secondary.light
},
gird: {
marginTop: "30px"
},
loadMore: {
textAlign: "right",
marginTop: "20px",
marginBottom: "40px"
},
badge: {
marginLeft: theme.spacing(1),
height: 17
},
orderSelect:{
textAlign:"right",
marginTop: 5,
}
});
const mapStateToProps = state => {
return {};
};
const mapDispatchToProps = dispatch => {
return {
toggleSnackbar: (vertical, horizontal, msg, color) => {
dispatch(toggleSnackbar(vertical, horizontal, msg, color));
}
};
};
class SearchComponent extends Component {
state = {
page: 1,
total: 0,
shareList: [],
showPwd: null,
orderBy:"created_at DESC",
};
componentDidMount = () => {
this.loadList(1,this.state.orderBy);
};
showPwd = pwd => {
this.setState({ showPwd: pwd });
};
handleClose = () => {
this.setState({ showPwd: null });
};
removeShare = (id) => {
API
.delete("/share/"+id)
.then(response => {
let oldList = this.state.shareList;
oldList = oldList.filter(value => {
return value.key !== id;
});
this.setState({
shareList: oldList,
total:this.state.total-1,
});
this.props.toggleSnackbar(
"top",
"right",
"分享已取消",
"success"
);
if (oldList.length === 0){
this.loadList(1,this.state.orderBy);
}
})
.catch(error => {
this.props.toggleSnackbar("top", "right", error.message, "error");
});
};
changePermission = id => {
let newPwd = Math.random()
.toString(36)
.substr(2)
.slice(2, 8);
let oldList = this.state.shareList;
let shareIndex = oldList.findIndex(value => {
return value.key === id;
});
API
.patch("/share/"+id, {
prop:"password",
value:oldList[shareIndex].password === "" ? newPwd : "",
})
.then(response => {
oldList[shareIndex].password = response.data;
this.setState({
shareList: oldList
});
})
.catch(error => {
this.props.toggleSnackbar("top", "right", error.message, "error");
});
};
changePreviewOption = id => {
let oldList = this.state.shareList;
let shareIndex = oldList.findIndex(value => {
return value.key === id;
});
API
.patch("/share/"+id, {
prop:"preview_enabled",
value:oldList[shareIndex].preview?"false":"true",
})
.then(response => {
oldList[shareIndex].preview = response.data;
this.setState({
shareList: oldList
});
})
.catch(error => {
this.props.toggleSnackbar("top", "right", error.message, "error");
});
};
loadList = (page,orderBy) => {
let order = orderBy.split(" ");
API.get("/share?page=" + page + "&order_by=" + order[0] + "&order=" + order[1])
.then(response => {
if (response.data.items.length === 0) {
this.props.toggleSnackbar(
"top",
"right",
"没有更多了",
"info"
);
}
this.setState({
total: response.data.total,
shareList: response.data.items
});
})
.catch(error => {
this.props.toggleSnackbar("top", "right", "加载失败", "error");
});
};
handlePageChange = (event, value) => {
this.setState({
page: value
});
this.loadList(value,this.state.orderBy);
};
handleOrderChange = event => {
this.setState({
orderBy:event.target.value,
});
this.loadList(this.state.page,event.target.value);
};
isExpired = share => {
return share.expire === 0 || share.remain_downloads === 0;
};
render() {
const { classes } = this.props;
return (
<div className={classes.layout}>
<Grid container>
<Grid sm={6} xs={6}>
<Typography color="textSecondary" variant="h4">
搜索结果
</Typography>
</Grid>
<Grid sm={6} xs={6} className={classes.orderSelect}>
<FormControl>
<Select color={"secondary"} onChange={this.handleOrderChange} value={this.state.orderBy}>
<MenuItem value={"created_at DESC"}>创建日期由晚到早</MenuItem>
<MenuItem value={"created_at ASC"}>创建日期由早到晚</MenuItem>
<MenuItem value={"downloads DESC"}>下载次数由大到小</MenuItem>
<MenuItem value={"downloads ASC"}>下载次数由小到大</MenuItem>
<MenuItem value={"views DESC"}>浏览次数由大到小</MenuItem>
<MenuItem value={"views ASC"}>浏览次数由小到大</MenuItem>
</Select>
</FormControl>
</Grid>
</Grid>
<Grid container spacing={24} className={classes.gird}>
{this.state.shareList.map(value => (
<Grid
item
xs={12}
sm={4}
key={value.id}
className={classes.cardContainer}
>
<Card className={classes.card}>
<CardHeader
avatar={
<div>
{!value.is_dir && (
<TypeIcon
fileName={
value.source
? value.source.name
: ""
}
isUpload
/>
)}{" "}
{value.is_dir && (
<Avatar
className={
classes.avatarFolder
}
>
<FolderIcon />
</Avatar>
)}
</div>
}
title={
<Tooltip
placement="top"
title={
value.source
? value.source.name
: "[原始对象不存在]"
}
>
<Typography
noWrap
className={classes.shareTitle}
>
{value.source
? value.source.name
: "[原始对象不存在]"}{" "}
</Typography>
</Tooltip>
}
subheader={
<span>
{value.create_date}
{this.isExpired(value) && (
<Chip
size="small"
className={classes.badge}
label="已失效"
/>
)}
</span>
}
/>
</Card>
</Grid>
))}
</Grid>
<div className={classes.loadMore}>
<Pagination
count={Math.ceil(this.state.total / 18)}
onChange={this.handlePageChange}
color="secondary"
/>
</div>{" "}
</div>
);
}
}
const SearchResult = connect(
mapStateToProps,
mapDispatchToProps
)(withStyles(styles)(withRouter(SearchComponent)));
export default SearchResult;

View File

@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import Navbar from "../component/Navbar/Navbar.js"
import AlertBar from "../component/Snackbar"
import { createMuiTheme } from '@material-ui/core/styles';
import MyShare from '../component/MyShare'
import MyShare from '../component/Share/MyShare'
import { CssBaseline, withStyles, MuiThemeProvider } from '@material-ui/core';
const theme = createMuiTheme(window.colorTheme);
const styles = theme => ({

View File

@ -1123,15 +1123,16 @@
dependencies:
"@babel/runtime" "^7.4.4"
"@material-ui/lab@^4.0.0-alpha.33":
version "4.0.0-alpha.33"
resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.33.tgz#3de96da1e813afcea5a07d56e54ec41cfa636e2c"
integrity sha512-+xttHUZLwH4+yfkB7B5A3pNDtQO27tGftuUxMRDMevNXwQtAB7mgYco34JwMr9kmvESYmacoZReOacJ6rxym/Q==
"@material-ui/lab@^4.0.0-alpha.42":
version "4.0.0-alpha.42"
resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.42.tgz#f8789d3ba39a1e5a13f462d618c2eec53f87ae10"
integrity sha512-JbKEMIXSslh03u6HNU1Pp1VXd9ycJ1dqkI+iQK6yR+Sng2mvMKzJ80GCV5ROXAXwwNnD8zHOopLZNIpTsEAVgQ==
dependencies:
"@babel/runtime" "^7.4.4"
"@material-ui/utils" "^4.5.2"
"@material-ui/utils" "^4.7.1"
clsx "^1.0.4"
prop-types "^15.7.2"
react-is "^16.8.0"
"@material-ui/styles@^4.9.0":
version "4.9.0"
@ -1169,15 +1170,6 @@
resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.0.0.tgz#26d6259dc6b39f4c2e1e9aceff7a11e031941741"
integrity sha512-UeH2BuKkwDndtMSS0qgx1kCzSMw+ydtj0xx/XbFtxNSTlXydKwzs5gVW5ZKsFlAkwoOOQ9TIsyoCC8hq18tOwg==
"@material-ui/utils@^4.5.2":
version "4.5.2"
resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.5.2.tgz#4c2fb531d357cf0da8cece53b588dff9b0bde934"
integrity sha512-zhbNfHd1gLa8At6RPDG7uMZubHxbY+LtM6IkSfeWi6Lo4Ax80l62YaN1QmUpO1IvGCkn/j62tQX3yObiQZrJsQ==
dependencies:
"@babel/runtime" "^7.4.4"
prop-types "^15.7.2"
react-is "^16.8.6"
"@material-ui/utils@^4.7.1":
version "4.7.1"
resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.7.1.tgz#dc16c7f0d2cd02fbcdd5cfe601fd6863ae3cc652"
@ -8640,7 +8632,7 @@ react-hotkeys@^2.0.0:
dependencies:
prop-types "^15.6.1"
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.6, react-is@^16.9.0:
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.9.0:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==