mirror of
https://github.com/cloudreve/frontend.git
synced 2025-12-25 19:52:48 +00:00
feature: 为弹窗类型的路径选择器PathSelector添加排序功能 (#164)
* feat: extract component `Sort` * feat: add sorting feature to `PathSelector` * feat: replace sort action with compoent `Sort` * feat: move `sortMethodFuncs` to module `Sort`
This commit is contained in:
parent
aabb964a8a
commit
9ccf148b31
|
|
@ -3,7 +3,6 @@ import { IconButton, makeStyles, Menu, MenuItem } from "@material-ui/core";
|
|||
import ViewListIcon from "@material-ui/icons/ViewList";
|
||||
import ViewSmallIcon from "@material-ui/icons/ViewComfy";
|
||||
import ViewModuleIcon from "@material-ui/icons/ViewModule";
|
||||
import TextTotateVerticalIcon from "@material-ui/icons/TextRotateVertical";
|
||||
import DownloadIcon from "@material-ui/icons/CloudDownload";
|
||||
import Avatar from "@material-ui/core/Avatar";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
|
@ -14,6 +13,7 @@ import { FormatPageBreak } from "mdi-material-ui";
|
|||
import pathHelper from "../../../utils/page";
|
||||
import { changePageSize } from "../../../redux/viewUpdate/action";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Sort from "../Sort";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
sideButton: {
|
||||
|
|
@ -22,17 +22,6 @@ const useStyles = makeStyles((theme) => ({
|
|||
},
|
||||
}));
|
||||
|
||||
const sortOptions = [
|
||||
"A-Z",
|
||||
"Z-A",
|
||||
"oldestUploaded",
|
||||
"newestUploaded",
|
||||
"oldestModified",
|
||||
"newestModified",
|
||||
"smallest",
|
||||
"largest",
|
||||
];
|
||||
|
||||
const paginationOption = ["50", "100", "200", "500", "1000"];
|
||||
|
||||
export default function SubActions({ isSmall, inherit }) {
|
||||
|
|
@ -63,29 +52,14 @@ export default function SubActions({ isSmall, inherit }) {
|
|||
const ChangePageSize = useCallback((e) => dispatch(changePageSize(e)), [
|
||||
dispatch,
|
||||
]);
|
||||
const [anchorSort, setAnchorSort] = useState(null);
|
||||
const [anchorPagination, setAnchorPagination] = useState(null);
|
||||
const [selectedIndex, setSelectedIndex] = useState(0);
|
||||
const showSortOptions = (e) => {
|
||||
setAnchorSort(e.currentTarget);
|
||||
};
|
||||
const showPaginationOptions = (e) => {
|
||||
setAnchorPagination(e.currentTarget);
|
||||
};
|
||||
const handleMenuItemClick = (e, index) => {
|
||||
setSelectedIndex(index);
|
||||
const optionsTable = {
|
||||
0: "namePos",
|
||||
1: "nameRev",
|
||||
2: "timePos",
|
||||
3: "timeRev",
|
||||
4: "modifyTimePos",
|
||||
5: "modifyTimeRev",
|
||||
6: "sizePos",
|
||||
7: "sizeRes",
|
||||
};
|
||||
ChangeSortMethod(optionsTable[index]);
|
||||
setAnchorSort(null);
|
||||
|
||||
/** change sort */
|
||||
const onChangeSort = (value) => {
|
||||
ChangeSortMethod(value);
|
||||
};
|
||||
const handlePaginationChange = (s) => {
|
||||
ChangePageSize(s);
|
||||
|
|
@ -181,32 +155,12 @@ export default function SubActions({ isSmall, inherit }) {
|
|||
</MenuItem>
|
||||
</Menu>
|
||||
|
||||
<IconButton
|
||||
title={t("sortMethod")}
|
||||
<Sort
|
||||
isSmall={isSmall}
|
||||
inherit={inherit}
|
||||
className={classes.sideButton}
|
||||
onClick={showSortOptions}
|
||||
color={inherit ? "inherit" : "default"}
|
||||
>
|
||||
<TextTotateVerticalIcon
|
||||
fontSize={isSmall ? "small" : "default"}
|
||||
/>
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="sort-menu"
|
||||
anchorEl={anchorSort}
|
||||
open={Boolean(anchorSort)}
|
||||
onClose={() => setAnchorSort(null)}
|
||||
>
|
||||
{sortOptions.map((option, index) => (
|
||||
<MenuItem
|
||||
key={option}
|
||||
selected={index === selectedIndex}
|
||||
onClick={(event) => handleMenuItemClick(event, index)}
|
||||
>
|
||||
{t("sortMethods." + option)}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
onChange={onChangeSort}
|
||||
/>
|
||||
{share && (
|
||||
<IconButton
|
||||
title={t("shareCreateBy", { nick: share.creator.nick })}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
MenuList,
|
||||
withStyles,
|
||||
} from "@material-ui/core";
|
||||
import Sort, { sortMethodFuncs } from './Sort';
|
||||
import API from "../../middleware/Api";
|
||||
import { toggleSnackbar } from "../../redux/explorer";
|
||||
import { withTranslation } from "react-i18next";
|
||||
|
|
@ -53,14 +54,27 @@ const styles = (theme) => ({
|
|||
maxHeight: "330px",
|
||||
overflowY: " auto",
|
||||
},
|
||||
sortWrapper: {
|
||||
textAlign: "right",
|
||||
paddingRight: "30px",
|
||||
},
|
||||
sortButton: {
|
||||
padding: "0",
|
||||
},
|
||||
});
|
||||
|
||||
class PathSelectorCompoment extends Component {
|
||||
state = {
|
||||
presentPath: "/",
|
||||
sortBy: '',
|
||||
dirList: [],
|
||||
selectedTarget: null,
|
||||
};
|
||||
/**
|
||||
* the source dir list from api `/directory`
|
||||
*
|
||||
* `state.dirList` is a sorted copy of it
|
||||
*/
|
||||
sourceDirList = []
|
||||
|
||||
componentDidMount = () => {
|
||||
const toBeLoad = this.props.presentPath;
|
||||
|
|
@ -93,31 +107,11 @@ class PathSelectorCompoment extends Component {
|
|||
dirList.forEach((value) => {
|
||||
value.displayName = value.name;
|
||||
});
|
||||
if (toBeLoad === "/") {
|
||||
dirList.unshift({ name: "/", path: "", displayName: "/" });
|
||||
} else {
|
||||
let path = toBeLoad;
|
||||
let name = toBeLoad;
|
||||
const displayNames = ["fileManager.currentFolder", "fileManager.backToParentFolder"];
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const paths = path.split("/");
|
||||
name = paths.pop();
|
||||
name = name === "" ? "/" : name;
|
||||
path = paths.join("/");
|
||||
dirList.unshift({
|
||||
name: name,
|
||||
path: path,
|
||||
displayName: this.props.t(
|
||||
displayNames[i]
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
this.sourceDirList = dirList
|
||||
this.setState({
|
||||
presentPath: toBeLoad,
|
||||
dirList: dirList,
|
||||
selectedTarget: null,
|
||||
});
|
||||
}, this.updateDirList);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.props.toggleSnackbar(
|
||||
|
|
@ -134,6 +128,51 @@ class PathSelectorCompoment extends Component {
|
|||
this.props.onSelect(this.state.dirList[index]);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* change sort type
|
||||
* @param {Event} event
|
||||
*/
|
||||
onChangeSort = (sortBy) => {
|
||||
this.setState({ sortBy }, this.updateDirList)
|
||||
};
|
||||
|
||||
/**
|
||||
* sort dir list, and handle parent dirs
|
||||
*/
|
||||
updateDirList = () => {
|
||||
const { state, sourceDirList } = this
|
||||
const { sortBy, presentPath } = state
|
||||
|
||||
// copy
|
||||
const dirList = [...sourceDirList]
|
||||
// sort
|
||||
const sortMethod = sortMethodFuncs[sortBy]
|
||||
if (sortMethod) dirList.sort(sortMethod)
|
||||
|
||||
// add root/parent dirs to top
|
||||
if (presentPath === "/") {
|
||||
dirList.unshift({ name: "/", path: "", displayName: "/" });
|
||||
} else {
|
||||
let path = presentPath;
|
||||
let name = presentPath;
|
||||
const displayNames = ["fileManager.currentFolder", "fileManager.backToParentFolder"];
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const paths = path.split("/");
|
||||
name = paths.pop();
|
||||
name = name === "" ? "/" : name;
|
||||
path = paths.join("/");
|
||||
dirList.unshift({
|
||||
name: name,
|
||||
path: path,
|
||||
displayName: this.props.t(
|
||||
displayNames[i]
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
this.setState({ dirList })
|
||||
}
|
||||
render() {
|
||||
const { classes, t } = this.props;
|
||||
|
||||
|
|
@ -157,6 +196,9 @@ class PathSelectorCompoment extends Component {
|
|||
|
||||
return (
|
||||
<div className={classes.container}>
|
||||
<div className={classes.sortWrapper}>
|
||||
<Sort value={this.state.sortBy} isSmall className={classes.sortButton} onChange={this.onChangeSort} />
|
||||
</div>
|
||||
<MenuList className={classes.selector}>
|
||||
{this.state.dirList.map((value, index) => (
|
||||
<MenuItem
|
||||
|
|
|
|||
|
|
@ -0,0 +1,105 @@
|
|||
import React, { MouseEventHandler, useState } from "react";
|
||||
import { IconButton, Menu, MenuItem } from "@material-ui/core";
|
||||
import TextTotateVerticalIcon from "@material-ui/icons/TextRotateVertical";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CloudreveFile, SortMethod } from "./../../types/index";
|
||||
|
||||
const SORT_OPTIONS: {
|
||||
value: SortMethod;
|
||||
label: string;
|
||||
}[] = [
|
||||
{ value: "namePos", label: "A-Z" },
|
||||
{ value: "nameRev", label: "Z-A" },
|
||||
{ value: "timePos", label: "oldestUploaded" },
|
||||
{ value: "timeRev", label: "newestUploaded" },
|
||||
{ value: "modifyTimePos", label: "oldestModified" },
|
||||
{ value: "modifyTimeRev", label: "newestModified" },
|
||||
{ value: "sizePos", label: "smallest" },
|
||||
{ value: "sizeRes", label: "largest" },
|
||||
]
|
||||
|
||||
export default function Sort({ value, onChange, isSmall, inherit, className }) {
|
||||
const { t } = useTranslation("application", { keyPrefix: "fileManager.sortMethods" });
|
||||
|
||||
const [anchorSort, setAnchorSort] = useState<Element | null>(null);
|
||||
const showSortOptions: MouseEventHandler<HTMLButtonElement> = (e) => {
|
||||
setAnchorSort(e.currentTarget);
|
||||
}
|
||||
|
||||
const [sortBy, setSortBy] = useState<SortMethod>(value || '')
|
||||
function onChangeSort(value: SortMethod) {
|
||||
setSortBy(value)
|
||||
onChange(value)
|
||||
setAnchorSort(null);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
title={t("sortMethod")}
|
||||
className={className}
|
||||
onClick={showSortOptions}
|
||||
color={inherit ? "inherit" : "default"}
|
||||
>
|
||||
<TextTotateVerticalIcon
|
||||
fontSize={isSmall ? "small" : "default"}
|
||||
/>
|
||||
</IconButton>
|
||||
<Menu
|
||||
id="sort-menu"
|
||||
anchorEl={anchorSort}
|
||||
open={Boolean(anchorSort)}
|
||||
onClose={() => setAnchorSort(null)}
|
||||
>
|
||||
{
|
||||
SORT_OPTIONS.map((option, index) => (
|
||||
<MenuItem
|
||||
key={index}
|
||||
selected={option.value === sortBy}
|
||||
onClick={() => onChangeSort(option.value)}
|
||||
>
|
||||
{t(option.label)}
|
||||
</MenuItem>
|
||||
))
|
||||
}
|
||||
</Menu>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
type SortFunc = (a: CloudreveFile, b: CloudreveFile) => number;
|
||||
|
||||
export const sortMethodFuncs: Record<SortMethod, SortFunc> = {
|
||||
sizePos: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return a.size - b.size;
|
||||
},
|
||||
sizeRes: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return b.size - a.size;
|
||||
},
|
||||
namePos: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return a.name.localeCompare(
|
||||
b.name,
|
||||
navigator.languages[0] || navigator.language,
|
||||
{ numeric: true, ignorePunctuation: true }
|
||||
);
|
||||
},
|
||||
nameRev: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return b.name.localeCompare(
|
||||
a.name,
|
||||
navigator.languages[0] || navigator.language,
|
||||
{ numeric: true, ignorePunctuation: true }
|
||||
);
|
||||
},
|
||||
timePos: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return Date.parse(a.create_date) - Date.parse(b.create_date);
|
||||
},
|
||||
timeRev: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return Date.parse(b.create_date) - Date.parse(a.create_date);
|
||||
},
|
||||
modifyTimePos: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return Date.parse(a.date) - Date.parse(b.date);
|
||||
},
|
||||
modifyTimeRev: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return Date.parse(b.date) - Date.parse(a.date);
|
||||
},
|
||||
};
|
||||
|
|
@ -17,7 +17,7 @@ import { Launch, PlaylistPlay, Subtitles } from "@material-ui/icons";
|
|||
import TextLoading from "../Placeholder/TextLoading";
|
||||
import SelectMenu from "./SelectMenu";
|
||||
import { getDownloadURL } from "../../services/file";
|
||||
import { sortMethodFuncs } from "../../redux/explorer/action";
|
||||
import { sortMethodFuncs } from "../FileManager/Sort";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const Artplayer = React.lazy(() =>
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import {
|
|||
saveFileToFileSystemDirectory,
|
||||
verifyFileSystemRWPermission,
|
||||
} from "../../utils/filesystem";
|
||||
import { sortMethodFuncs } from "../../component/FileManager/Sort";
|
||||
|
||||
export interface ActionSetFileList extends AnyAction {
|
||||
type: "SET_FILE_LIST";
|
||||
|
|
@ -113,42 +114,6 @@ export const setShiftSelectedIds = (shiftSelectedIds: any) => {
|
|||
};
|
||||
};
|
||||
|
||||
type SortFunc = (a: CloudreveFile, b: CloudreveFile) => number;
|
||||
export const sortMethodFuncs: Record<SortMethod, SortFunc> = {
|
||||
sizePos: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return a.size - b.size;
|
||||
},
|
||||
sizeRes: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return b.size - a.size;
|
||||
},
|
||||
namePos: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return a.name.localeCompare(
|
||||
b.name,
|
||||
navigator.languages[0] || navigator.language,
|
||||
{ numeric: true, ignorePunctuation: true }
|
||||
);
|
||||
},
|
||||
nameRev: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return b.name.localeCompare(
|
||||
a.name,
|
||||
navigator.languages[0] || navigator.language,
|
||||
{ numeric: true, ignorePunctuation: true }
|
||||
);
|
||||
},
|
||||
timePos: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return Date.parse(a.create_date) - Date.parse(b.create_date);
|
||||
},
|
||||
timeRev: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return Date.parse(b.create_date) - Date.parse(a.create_date);
|
||||
},
|
||||
modifyTimePos: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return Date.parse(a.date) - Date.parse(b.date);
|
||||
},
|
||||
modifyTimeRev: (a: CloudreveFile, b: CloudreveFile) => {
|
||||
return Date.parse(b.date) - Date.parse(a.date);
|
||||
},
|
||||
};
|
||||
|
||||
export const selectAll = (): ThunkAction<any, any, any, any> => {
|
||||
return (dispatch, getState): void => {
|
||||
const state = getState();
|
||||
|
|
|
|||
Loading…
Reference in New Issue