swapped over context menu and quick items to completly functional components

This commit is contained in:
subnub
2024-06-22 04:32:54 -04:00
parent 4cccd43677
commit 45bcd5a7d6
7 changed files with 508 additions and 461 deletions
@@ -1,124 +0,0 @@
import classNames from "classnames";
import React from "react";
import Swal from "sweetalert2";
import { deleteFile, renameFile } from "../../api/filesAPI";
import { useFiles, useQuickFiles } from "../../hooks/files";
const NewContextMenu = (props) => {
const { invalidateFilesCache } = useFiles();
const { invalidateQuickFilesCache } = useQuickFiles();
const liClassname =
"flex w-full px-[20px] py-[12px] items-center font-normal justify-start no-underline transition-all duration-400 ease-in-out hover:bg-[#f6f5fd] hover:text-[#3c85ee] hover:font-medium";
const spanClassname = "inline-flex mr-[18px]";
const renameItem = async () => {
if (!props.folderMode) {
const { value: filename } = await Swal.fire({
title: "Enter A File Name",
input: "text",
inputValue: props.file.filename,
showCancelButton: true,
inputValidator: (value) => {
if (!value) {
return "Please Enter a Name";
}
},
});
await renameFile(props.file._id, filename);
invalidateFilesCache();
invalidateQuickFilesCache();
}
};
const deleteItem = async () => {
if (!props.folderMode) {
const result = await Swal.fire({
title: "Confirm Deletion",
text: "You cannot undo this action",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete",
});
if (result.value) {
await deleteFile(props.file._id);
invalidateFilesCache();
invalidateQuickFilesCache();
}
}
};
return (
<div
onClick={props.stopPropagation}
ref={props.wrapperRef}
className={classNames(
"fixed min-w-[215px] bg-white shadow-[0px_2px_4px_rgba(0,0,0,0.15),_inset_0px_1px_0px_#f5f7fa] rounded-[4px] mt-[-5px] z-[2] animate-very-long",
props.contextSelected.selected ? "opacity-100" : "opacity-0"
)}
style={
props.contextSelected.selected
? {
display: "block",
left: `${props.contextSelected.X}px`,
top: `${props.contextSelected.Y}px`,
}
: { display: "none" }
}
>
<ul className="p-0 list-none m-0 ">
<li onClick={renameItem} className={liClassname}>
<a className="flex">
<span className={spanClassname}>
<img src="/assets/filesetting1.svg" alt="setting" />
</span>
Rename
</a>
</li>
{!props.folderMode ? (
<li onClick={props.startShareFile} className={liClassname}>
<a className="flex" data-modal="share__modal">
<span
className="inline-flex mr-[18px]
"
>
<img src="/assets/filesetting2.svg" alt="setting" />
</span>
Share
</a>
</li>
) : undefined}
{!props.folderMode ? (
<li onClick={props.startFileDownload} className={liClassname}>
<a className="flex">
<span className={spanClassname}>
<img src="/assets/filesetting3.svg" alt="setting" />
</span>
Download
</a>
</li>
) : undefined}
<li onClick={props.startMovingFile} className={liClassname}>
<a className="flex" data-modal="destination__modal">
<span className={spanClassname}>
<img src="/assets/filesetting4.svg" alt="setting" />
</span>{" "}
Move
</a>
</li>
<li onClick={deleteItem} className={liClassname}>
<a className="flex">
<span className={spanClassname}>
<img src="/assets/filesetting5.svg" alt="setting" />
</span>
Delete
</a>
</li>
</ul>
</div>
);
};
export default NewContextMenu;
+150 -61
View File
@@ -1,73 +1,162 @@
import React from "react";
import { connect } from "react-redux";
import { setShareSelected } from "../../actions/selectedItem";
import NewContextMenu from "./NewContextMenu";
import classNames from "classnames";
import React, { useEffect, useRef } from "react";
import Swal from "sweetalert2";
import { deleteFile, renameFile } from "../../api/filesAPI";
import { useFiles, useQuickFiles } from "../../hooks/files";
class NewContextMenuContainer extends React.Component {
constructor(props) {
super(props);
const NewContextMenu = (props) => {
const { invalidateFilesCache } = useFiles();
const { invalidateQuickFilesCache } = useQuickFiles();
const wrapperRef = useRef();
const liClassname =
"flex w-full px-[20px] py-[12px] items-center font-normal justify-start no-underline transition-all duration-400 ease-in-out hover:bg-[#f6f5fd] hover:text-[#3c85ee] hover:font-medium";
const spanClassname = "inline-flex mr-[18px]";
this.wrapperRef = React.createRef();
}
componentDidMount = () => {
document.addEventListener("mousedown", this.handleClickOutside);
};
componentWillUnmount = () => {
document.removeEventListener("mousedown", this.handleClickOutside);
};
componentDidUpdate = () => {};
handleClickOutside = () => {
if (
this.props.contextSelected.selected &&
this.wrapperRef &&
!this.wrapperRef.current.contains(event.target)
) {
this.props.closeContext();
const renameItem = async () => {
if (!props.folderMode) {
const { value: filename } = await Swal.fire({
title: "Enter A File Name",
input: "text",
inputValue: props.file.filename,
showCancelButton: true,
inputValidator: (value) => {
if (!value) {
return "Please Enter a Name";
}
},
});
await renameFile(props.file._id, filename);
invalidateFilesCache();
invalidateQuickFilesCache();
}
};
startFileDownload = () => {
console.log("start download file", this.props.file._id);
this.props.downloadFile(this.props.file._id, this.props.file);
const deleteItem = async () => {
if (!props.folderMode) {
const result = await Swal.fire({
title: "Confirm Deletion",
text: "You cannot undo this action",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete",
});
if (result.value) {
await deleteFile(props.file._id);
invalidateFilesCache();
invalidateQuickFilesCache();
}
}
};
startRenameFile = async (e) => {
console.log("rename file click");
this.props.changeEditNameMode();
// startMovingFile = async () => {
// this.props.dispatch(
// setMoverID(
// this.props._id,
// this.props.metadata.parent,
// true,
// this.props.metadata.drive,
// this.props.metadata.personalFile
// )
// );
// };
// startShareFile = () => {
// this.props.dispatch(setShareSelected({ ...this.props.file }));
// };
// startFileDownload = () => {
// console.log("start download file", this.props.file._id);
// this.props.downloadFile(this.props.file._id, this.props.file);
// };
const outOfBoundsClickCheck = () => {
if (wrapperRef && !wrapperRef.current.contains(event.target)) {
props.closeContext();
}
};
startDeleteFile = async () => {
this.props.changeDeleteMode();
};
useEffect(() => {
document.addEventListener("mousedown", outOfBoundsClickCheck);
document.addEventListener("touchstart", outOfBoundsClickCheck);
startShareFile = () => {
this.props.dispatch(setShareSelected({ ...this.props.file }));
};
return () => {
document.removeEventListener("mousedown", outOfBoundsClickCheck);
document.removeEventListener("touchstart", outOfBoundsClickCheck);
};
}, []);
stopPropagation = (e) => {
e.stopPropagation();
if (this.props.quickItemMode || this.props.gridMode)
this.props.closeContext();
};
return (
<div
onClick={props.stopPropagation}
ref={wrapperRef}
className={classNames(
"fixed min-w-[215px] bg-white shadow-[0px_2px_4px_rgba(0,0,0,0.15),_inset_0px_1px_0px_#f5f7fa] rounded-[4px] mt-[-5px] z-[2] animate-very-long",
props.contextSelected.selected ? "opacity-100" : "opacity-0"
)}
style={
props.contextSelected.selected
? {
display: "block",
left: `${props.contextSelected.X}px`,
top: `${props.contextSelected.Y}px`,
}
: { display: "none" }
}
>
<ul className="p-0 list-none m-0 ">
<li onClick={renameItem} className={liClassname}>
<a className="flex">
<span className={spanClassname}>
<img src="/assets/filesetting1.svg" alt="setting" />
</span>
Rename
</a>
</li>
{!props.folderMode ? (
<li onClick={props.startShareFile} className={liClassname}>
<a className="flex" data-modal="share__modal">
<span
className="inline-flex mr-[18px]
"
>
<img src="/assets/filesetting2.svg" alt="setting" />
</span>
Share
</a>
</li>
) : undefined}
{!props.folderMode ? (
<li onClick={props.startFileDownload} className={liClassname}>
<a className="flex">
<span className={spanClassname}>
<img src="/assets/filesetting3.svg" alt="setting" />
</span>
Download
</a>
</li>
) : undefined}
<li onClick={props.startMovingFile} className={liClassname}>
<a className="flex" data-modal="destination__modal">
<span className={spanClassname}>
<img src="/assets/filesetting4.svg" alt="setting" />
</span>{" "}
Move
</a>
</li>
<li onClick={deleteItem} className={liClassname}>
<a className="flex">
<span className={spanClassname}>
<img src="/assets/filesetting5.svg" alt="setting" />
</span>
Delete
</a>
</li>
</ul>
</div>
);
};
render() {
return (
<NewContextMenu
wrapperRef={this.wrapperRef}
stopPropagation={this.stopPropagation}
startRenameFile={this.startRenameFile}
startShareFile={this.startShareFile}
startFileDownload={this.startFileDownload}
startDeleteFile={this.startDeleteFile}
state={this.state}
{...this.props}
/>
);
}
}
export default connect()(NewContextMenuContainer);
export default NewContextMenu;
+75
View File
@@ -0,0 +1,75 @@
import React from "react";
import { connect } from "react-redux";
import { setShareSelected } from "../../actions/selectedItem";
import NewContextMenu from ".";
class NewContextMenuContainer extends React.Component {
constructor(props) {
super(props);
this.wrapperRef = React.createRef();
}
// componentDidMount = () => {
// document.addEventListener("mousedown", this.handleClickOutside);
// document.addEventListener("touchstart", this.handleClickOutside);
// };
// componentWillUnmount = () => {
// document.removeEventListener("mousedown", this.handleClickOutside);
// document.removeEventListener("touchstart", this.handleClickOutside);
// };
componentDidUpdate = () => {};
handleClickOutside = () => {
if (
this.props.contextSelected.selected &&
this.wrapperRef &&
!this.wrapperRef.current.contains(event.target)
) {
this.props.closeContext();
}
};
startFileDownload = () => {
console.log("start download file", this.props.file._id);
this.props.downloadFile(this.props.file._id, this.props.file);
};
startRenameFile = async (e) => {
console.log("rename file click");
this.props.changeEditNameMode();
};
startDeleteFile = async () => {
this.props.changeDeleteMode();
};
startShareFile = () => {
this.props.dispatch(setShareSelected({ ...this.props.file }));
};
stopPropagation = (e) => {
e.stopPropagation();
if (this.props.quickItemMode || this.props.gridMode)
this.props.closeContext();
};
// render() {
// return (
// <NewContextMenu
// wrapperRef={this.wrapperRef}
// stopPropagation={this.stopPropagation}
// startRenameFile={this.startRenameFile}
// startShareFile={this.startShareFile}
// startFileDownload={this.startFileDownload}
// startDeleteFile={this.startDeleteFile}
// state={this.state}
// {...this.props}
// />
// );
// }
}
export default connect()(NewContextMenuContainer);
@@ -1,127 +0,0 @@
import capitalize from "../../utils/capitalize";
import moment from "moment";
import React, { useMemo, useState } from "react";
import NewContextMenu from "../NewContextMenu";
import classNames from "classnames";
import { getFileColor, getFileExtension } from "../../utils/files";
import { useThumbnail } from "../../hooks/files";
import { useContextMenu } from "../../hooks/contextMenu";
const QuickAccessItem = (props) => {
const { image, hasThumbnail, imageOnError } = useThumbnail(
props.metadata.hasThumbnail,
props.metadata.thumbnailID
);
const { onContextMenu, closeContextMenu, ...contextMenuState } =
useContextMenu();
const fileExtension = useMemo(
() => getFileExtension(props.filename),
[props.filename]
);
const imageColor = useMemo(
() => getFileColor(props.filename),
[props.filename]
);
const elementSelected = useMemo(
() => `quick-${props._id}` === props.selected,
[props._id, props.selected]
);
return (
<div
className={classNames(
"border rounded-md o transition-all duration-400 ease-in-out cursor-pointer w-48 flex items-center justify-center flex-col h-[150px] animiate hover:border-[#3c85ee] overflow-hidden",
elementSelected ? "border-[#3c85ee]" : "border-[#ebe9f9]"
)}
onClick={() => {
props.fileClick(props._id, props, true);
}}
onContextMenu={onContextMenu}
onTouchStart={props.onTouchStart}
onTouchEnd={props.onTouchEnd}
onTouchMove={props.onTouchMove}
>
{contextMenuState.selected && (
<div onClick={props.clickStopPropagation}>
<NewContextMenu
gridMode={true}
quickItemMode={true}
contextSelected={contextMenuState}
closeContext={closeContextMenu}
downloadFile={props.downloadFile}
file={props}
changeEditNameMode={props.changeEditNameMode}
closeEditNameMode={props.closeEditNameMode}
changeDeleteMode={props.changeDeleteMode}
startMovingFile={props.startMovingFile}
/>
</div>
)}
<div
className={classNames(
"inline-flex items-center w-full bg-white relative",
{
"mt-2": !hasThumbnail,
}
)}
>
{hasThumbnail ? (
<img
className="w-full min-h-[88px] max-h-[88px] h-full flex object-cover"
src={image}
onError={imageOnError}
/>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
width="150"
height="150"
viewBox="0 0 24 24"
className="w-full min-h-[80px] max-h-[80px] h-full flex"
>
<path
d="M13,9V3.5L18.5,9M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6Z"
fill={imageColor}
/>
</svg>
)}
{!hasThumbnail && (
<div className="w-full h-full absolute flex justify-center items-center text-white mt-3">
<p className="text-sm">{fileExtension}</p>
</div>
)}
</div>
<div
className={classNames(
"p-3 overflow-hidden text-ellipsis block w-full animate",
elementSelected ? "bg-[#3c85ee]" : "bg-white"
)}
>
<p
className={classNames(
"m-0 text-[14px] leading-[16px] font-normal max-w-full overflow-hidden text-ellipsis whitespace-nowrap animate",
elementSelected ? "text-white" : "text-[#212b36]"
)}
>
{capitalize(props.filename)}
</p>
<span
className={classNames(
"m-0 text-[#637381] font-normal max-w-full whitespace-nowrap text-xs animate",
elementSelected ? "text-white" : "text-[#637381]"
)}
>
Created {moment(props.uploadDate).calendar()}
</span>
</div>
</div>
);
};
export default QuickAccessItem;
+127 -145
View File
@@ -1,152 +1,134 @@
import QuickAccessItem from "./QuickAccessItem";
import {
setRightSelected,
setLastSelected,
setSelected,
} from "../../actions/selectedItem";
import mobileCheck from "../../utils/mobileCheck";
import env from "../../enviroment/envFrontEnd";
import axios from "../../axiosInterceptor";
import { connect } from "react-redux";
import React from "react";
import Swal from "sweetalert2";
import { startRenameFile, startRemoveFile } from "../../actions/files";
import { setMoverID } from "../../actions/mover";
import mobilecheck from "../../utils/mobileCheck";
import { setMobileContextMenu } from "../../actions/mobileContextMenu";
import capitalize from "../../utils/capitalize";
import moment from "moment";
import React, { useMemo, useState } from "react";
import NewContextMenu from "../NewContextMenu";
import classNames from "classnames";
import { getFileColor, getFileExtension } from "../../utils/files";
import { useThumbnail } from "../../hooks/files";
import { useContextMenu } from "../../hooks/contextMenu";
const currentURL = env.url;
const QuickAccessItem = (props) => {
const { image, hasThumbnail, imageOnError } = useThumbnail(
props.metadata.hasThumbnail,
props.metadata.thumbnailID
);
class QuickAccessItemContainer extends React.Component {
constructor(props) {
super(props);
const {
onContextMenu,
closeContextMenu,
onTouchStart,
onTouchMove,
onTouchEnd,
clickStopPropagation,
...contextMenuState
} = useContextMenu();
this.failedToLoad = false;
this.lastTouch = 0;
const fileExtension = useMemo(
() => getFileExtension(props.filename),
[props.filename]
);
this.state = {
image: "/images/file-svg.svg",
imageClassname: "noSelect file__item-no-thumbnail",
contextSelected: false,
hasThumbnail: false,
};
}
const imageColor = useMemo(
() => getFileColor(props.filename),
[props.filename]
);
onTouchStart = () => {
const date = new Date();
this.lastTouch = date.getTime();
};
const elementSelected = useMemo(
() => `quick-${props._id}` === props.selected,
[props._id, props.selected]
);
onTouchMove = () => {
this.lastTouch = 0;
};
return (
<div
className={classNames(
"border rounded-md o transition-all duration-400 ease-in-out cursor-pointer w-48 flex items-center justify-center flex-col h-[150px] animiate hover:border-[#3c85ee] overflow-hidden",
elementSelected ? "border-[#3c85ee]" : "border-[#ebe9f9]"
)}
onClick={() => {
props.fileClick(props._id, props, true);
}}
onContextMenu={onContextMenu}
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
>
{contextMenuState.selected && (
<div onClick={clickStopPropagation}>
<NewContextMenu
gridMode={true}
quickItemMode={true}
contextSelected={contextMenuState}
closeContext={closeContextMenu}
downloadFile={props.downloadFile}
file={props}
changeEditNameMode={props.changeEditNameMode}
closeEditNameMode={props.closeEditNameMode}
changeDeleteMode={props.changeDeleteMode}
startMovingFile={props.startMovingFile}
/>
</div>
)}
<div
className={classNames(
"inline-flex items-center w-full bg-white relative",
{
"mt-2": !hasThumbnail,
}
)}
>
{hasThumbnail ? (
<img
className="w-full min-h-[88px] max-h-[88px] h-full flex object-cover"
src={image}
onError={imageOnError}
/>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1"
width="150"
height="150"
viewBox="0 0 24 24"
className="w-full min-h-[80px] max-h-[80px] h-full flex"
>
<path
d="M13,9V3.5L18.5,9M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6Z"
fill={imageColor}
/>
</svg>
)}
{!hasThumbnail && (
<div className="w-full h-full absolute flex justify-center items-center text-white mt-3">
<p className="text-sm">{fileExtension}</p>
</div>
)}
</div>
<div
className={classNames(
"p-3 overflow-hidden text-ellipsis block w-full animate",
elementSelected ? "bg-[#3c85ee]" : "bg-white"
)}
>
<p
className={classNames(
"m-0 text-[14px] leading-[16px] font-normal max-w-full overflow-hidden text-ellipsis whitespace-nowrap animate",
elementSelected ? "text-white" : "text-[#212b36]"
)}
>
{capitalize(props.filename)}
</p>
<span
className={classNames(
"m-0 text-[#637381] font-normal max-w-full whitespace-nowrap text-xs animate",
elementSelected ? "text-white" : "text-[#637381]"
)}
>
Created {moment(props.uploadDate).calendar()}
</span>
</div>
</div>
);
};
onTouchEnd = () => {
if (this.lastTouch === 0) {
return;
}
const date = new Date();
const difference = date - this.lastTouch;
this.lastTouch = 0;
if (difference > 500) {
// this.selectContext();
}
};
changeEditNameMode = async () => {
let inputValue = this.props.filename;
const { value: folderName } = await Swal.fire({
title: "Enter A File Name",
input: "text",
inputValue: inputValue,
showCancelButton: true,
inputValidator: (value) => {
if (!value) {
return "Please Enter a Name";
}
},
});
if (folderName === undefined || folderName === null) {
return;
}
this.props.dispatch(
startRenameFile(this.props._id, folderName, this.props.metadata.drive)
);
};
closeEditNameMode = () => {
this.setState(() => {
return {
...this.state,
editNameMode: false,
};
});
};
changeDeleteMode = async () => {
Swal.fire({
title: "Confirm Deletion",
text: "You cannot undo this action",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete",
}).then((result) => {
if (result.value) {
this.props.dispatch(
startRemoveFile(
this.props._id,
this.props.metadata.drive,
this.props.metadata.personalFile
)
);
}
});
};
startMovingFile = async () => {
this.props.dispatch(
setMoverID(
this.props._id,
this.props.metadata.parent,
true,
this.props.metadata.drive,
this.props.metadata.personalFile
)
);
};
clickStopPropagation = (e) => {
e.stopPropagation();
};
render() {
return (
<QuickAccessItem
onTouchStart={this.onTouchStart}
onTouchMove={this.onTouchMove}
onTouchEnd={this.onTouchEnd}
changeEditNameMode={this.changeEditNameMode}
closeEditNameMode={this.closeEditNameMode}
changeDeleteMode={this.changeDeleteMode}
startMovingFile={this.startMovingFile}
state={this.state}
{...this.props}
/>
);
}
}
const connectStateToProp = (state) => ({
rightSelected: state.selectedItem.rightSelected,
selected: state.selectedItem.selected,
});
export default connect(connectStateToProp)(QuickAccessItemContainer);
export default QuickAccessItem;
+117
View File
@@ -0,0 +1,117 @@
import QuickAccessItem from ".";
import {
setRightSelected,
setLastSelected,
setSelected,
} from "../../actions/selectedItem";
import mobileCheck from "../../utils/mobileCheck";
import env from "../../enviroment/envFrontEnd";
import axios from "../../axiosInterceptor";
import { connect } from "react-redux";
import React from "react";
import Swal from "sweetalert2";
import { startRenameFile, startRemoveFile } from "../../actions/files";
import { setMoverID } from "../../actions/mover";
import mobilecheck from "../../utils/mobileCheck";
import { setMobileContextMenu } from "../../actions/mobileContextMenu";
const currentURL = env.url;
class QuickAccessItemContainer extends React.Component {
constructor(props) {
super(props);
this.failedToLoad = false;
this.state = {
image: "/images/file-svg.svg",
imageClassname: "noSelect file__item-no-thumbnail",
contextSelected: false,
hasThumbnail: false,
};
}
changeEditNameMode = async () => {
let inputValue = this.props.filename;
const { value: folderName } = await Swal.fire({
title: "Enter A File Name",
input: "text",
inputValue: inputValue,
showCancelButton: true,
inputValidator: (value) => {
if (!value) {
return "Please Enter a Name";
}
},
});
if (folderName === undefined || folderName === null) {
return;
}
this.props.dispatch(
startRenameFile(this.props._id, folderName, this.props.metadata.drive)
);
};
closeEditNameMode = () => {
this.setState(() => {
return {
...this.state,
editNameMode: false,
};
});
};
changeDeleteMode = async () => {
Swal.fire({
title: "Confirm Deletion",
text: "You cannot undo this action",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete",
}).then((result) => {
if (result.value) {
this.props.dispatch(
startRemoveFile(
this.props._id,
this.props.metadata.drive,
this.props.metadata.personalFile
)
);
}
});
};
startMovingFile = async () => {
this.props.dispatch(
setMoverID(
this.props._id,
this.props.metadata.parent,
true,
this.props.metadata.drive,
this.props.metadata.personalFile
)
);
};
// render() {
// return (
// <QuickAccessItem
// startMovingFile={this.startMovingFile}
// state={this.state}
// {...this.props}
// />
// );
// }
}
const connectStateToProp = (state) => ({
rightSelected: state.selectedItem.rightSelected,
selected: state.selectedItem.selected,
});
export default connect(connectStateToProp)(QuickAccessItemContainer);