feat(fe/rows): add rows view into files panel
This commit is contained in:
parent
87832ee1b2
commit
3550a3a77d
11 changed files with 535 additions and 130 deletions
|
@ -135,7 +135,7 @@
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-default #browser-op button {
|
.theme-default #browser-op .left {
|
||||||
margin: 0 1rem 0 0;
|
margin: 0 1rem 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +163,36 @@
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.theme-default #item-rows {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-default #item-rows .name, .theme-default #item-rows .name a {
|
||||||
|
color: #34495e;
|
||||||
|
font-size: 1.8rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
display: block;
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-default #item-rows .desc {
|
||||||
|
color: #95a5a6;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-default #item-rows .hr {
|
||||||
|
height: 1px;
|
||||||
|
margin: 2rem 0;
|
||||||
|
background-color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-default #item-rows .card {
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
.theme-default .item-cell {
|
.theme-default .item-cell {
|
||||||
height: 5rem;
|
height: 5rem;
|
||||||
}
|
}
|
||||||
|
@ -349,7 +379,7 @@
|
||||||
color: #34495e;
|
color: #34495e;
|
||||||
background-color: #ecf0f6;
|
background-color: #ecf0f6;
|
||||||
border: solid 1px #95a5a6;
|
border: solid 1px #95a5a6;
|
||||||
margin: 0.5rem 0 1rem 0;
|
/* margin: 0.5rem 0 1rem 0; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.captcha {
|
.captcha {
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
"react": "^16.8.6",
|
"react": "^16.8.6",
|
||||||
"react-copy-to-clipboard": "^5.0.1",
|
"react-copy-to-clipboard": "^5.0.1",
|
||||||
"react-dom": "^16.8.6",
|
"react-dom": "^16.8.6",
|
||||||
|
"react-icons": "4.3.1",
|
||||||
"react-svg": "^8.0.6",
|
"react-svg": "^8.0.6",
|
||||||
"throttle-debounce": "^2.1.0",
|
"throttle-debounce": "^2.1.0",
|
||||||
"webpack-bundle-analyzer": "^4.4.2",
|
"webpack-bundle-analyzer": "^4.4.2",
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { MockSettingsClient } from "../../client/settings_mock";
|
||||||
import { controlName as panelTabs } from "../root_frame";
|
import { controlName as panelTabs } from "../root_frame";
|
||||||
import { settingsDialogCtrl } from "../layers";
|
import { settingsDialogCtrl } from "../layers";
|
||||||
import { settingsTabsCtrl } from "../dialog_settings";
|
import { settingsTabsCtrl } from "../dialog_settings";
|
||||||
|
import { filesViewCtrl } from "../panel_files";
|
||||||
|
|
||||||
describe("Login", () => {
|
describe("Login", () => {
|
||||||
initMockWorker();
|
initMockWorker();
|
||||||
|
@ -122,6 +123,7 @@ describe("Login", () => {
|
||||||
[settingsDialogCtrl]: ctrlOff,
|
[settingsDialogCtrl]: ctrlOff,
|
||||||
[settingsTabsCtrl]: "preferencePane",
|
[settingsTabsCtrl]: "preferencePane",
|
||||||
[sharingCtrl]: ctrlOff,
|
[sharingCtrl]: ctrlOff,
|
||||||
|
[filesViewCtrl]: "rows",
|
||||||
}),
|
}),
|
||||||
options: Map<string, Set<string>>({
|
options: Map<string, Set<string>>({
|
||||||
[panelTabs]: Set<string>([
|
[panelTabs]: Set<string>([
|
||||||
|
@ -132,6 +134,7 @@ describe("Login", () => {
|
||||||
[settingsDialogCtrl]: Set<string>([ctrlOn, ctrlOff]),
|
[settingsDialogCtrl]: Set<string>([ctrlOn, ctrlOff]),
|
||||||
[settingsTabsCtrl]: Set<string>(["preferencePane", "managementPane"]),
|
[settingsTabsCtrl]: Set<string>(["preferencePane", "managementPane"]),
|
||||||
[sharingCtrl]: Set<string>([ctrlOn, ctrlOff]),
|
[sharingCtrl]: Set<string>([ctrlOn, ctrlOff]),
|
||||||
|
[filesViewCtrl]: Set<string>(["rows", "table"]),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { UploadEntry } from "../worker/interface";
|
||||||
import { MsgPackage } from "../i18n/msger";
|
import { MsgPackage } from "../i18n/msger";
|
||||||
import { User, MetadataResp } from "../client";
|
import { User, MetadataResp } from "../client";
|
||||||
import { settingsDialogCtrl } from "./layers";
|
import { settingsDialogCtrl } from "./layers";
|
||||||
|
import { filesViewCtrl } from "./panel_files";
|
||||||
import { FilesProps } from "./panel_files";
|
import { FilesProps } from "./panel_files";
|
||||||
import { UploadingsProps } from "./panel_uploadings";
|
import { UploadingsProps } from "./panel_uploadings";
|
||||||
import { SharingsProps } from "./panel_sharings";
|
import { SharingsProps } from "./panel_sharings";
|
||||||
|
@ -112,6 +113,7 @@ export function initState(): ICoreState {
|
||||||
[settingsDialogCtrl]: "off",
|
[settingsDialogCtrl]: "off",
|
||||||
[settingsTabsCtrl]: "preferencePane",
|
[settingsTabsCtrl]: "preferencePane",
|
||||||
[sharingCtrl]: "off",
|
[sharingCtrl]: "off",
|
||||||
|
[filesViewCtrl]: "rows",
|
||||||
}),
|
}),
|
||||||
options: Map<string, Set<string>>({
|
options: Map<string, Set<string>>({
|
||||||
[panelTabs]: Set<string>([
|
[panelTabs]: Set<string>([
|
||||||
|
@ -122,6 +124,7 @@ export function initState(): ICoreState {
|
||||||
[settingsDialogCtrl]: Set<string>(["on", "off"]),
|
[settingsDialogCtrl]: Set<string>(["on", "off"]),
|
||||||
[settingsTabsCtrl]: Set<string>(["preferencePane", "managementPane"]),
|
[settingsTabsCtrl]: Set<string>(["preferencePane", "managementPane"]),
|
||||||
[sharingCtrl]: Set<string>(["on", "off"]),
|
[sharingCtrl]: Set<string>(["on", "off"]),
|
||||||
|
[filesViewCtrl]: Set<string>(["rows", "table"]),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
123
src/client/web/src/components/layout/rows.tsx
Normal file
123
src/client/web/src/components/layout/rows.tsx
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import { List, Map, Set } from "immutable";
|
||||||
|
|
||||||
|
import { RiArrowUpDownFill } from "@react-icons/all-files/ri/RiArrowUpDownFill";
|
||||||
|
|
||||||
|
import { Flexbox } from "./flexbox";
|
||||||
|
|
||||||
|
export interface Row {
|
||||||
|
elem: React.ReactNode; // element to display
|
||||||
|
val: Object; // original object value
|
||||||
|
sortVals: List<string>; // sortable values in order
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
sortKeys: List<string>; // display names in order for sorting
|
||||||
|
rows: List<Row>;
|
||||||
|
id?: string;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
className?: string;
|
||||||
|
updateRows?: (rows: Object) => void; // this is a callback which update state with re-sorted rows
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
orders: List<boolean>; // asc = true, desc = false
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Rows extends React.Component<Props, State, {}> {
|
||||||
|
constructor(p: Props) {
|
||||||
|
super(p);
|
||||||
|
this.state = {
|
||||||
|
orders: p.sortKeys.map((_: string, i: number) => {
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sortRows = (key: number) => {
|
||||||
|
if (this.props.updateRows == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const sortOption = this.props.sortKeys.get(key);
|
||||||
|
if (sortOption == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentOrder = this.state.orders.get(key);
|
||||||
|
if (currentOrder == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const expectedOrder = !currentOrder;
|
||||||
|
|
||||||
|
const sortedRows = this.props.rows.sort((row1: Row, row2: Row) => {
|
||||||
|
const val1 = row1.sortVals.get(key);
|
||||||
|
const val2 = row2.sortVals.get(key);
|
||||||
|
if (val1 == null || val2 == null) {
|
||||||
|
// elements without the sort key will be moved to the last
|
||||||
|
if (val1 == null && val2 != null) {
|
||||||
|
return 1;
|
||||||
|
} else if (val1 != null && val2 == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else if (val1 < val2) {
|
||||||
|
return expectedOrder ? -1 : 1;
|
||||||
|
} else if (val1 === val2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return expectedOrder ? 1 : -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortedItems = sortedRows.map((row: Row): Object => {
|
||||||
|
return row.val;
|
||||||
|
});
|
||||||
|
const newOrders = this.state.orders.set(key, !currentOrder);
|
||||||
|
this.setState({ orders: newOrders });
|
||||||
|
this.props.updateRows(sortedItems);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const sortBtns = this.props.sortKeys.map(
|
||||||
|
(displayName: string, i: number): React.ReactNode => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={`rows-${i}`}
|
||||||
|
className="float"
|
||||||
|
onClick={() => {
|
||||||
|
this.sortRows(i);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{displayName}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const bodyRows = this.props.rows.map(
|
||||||
|
(row: Row, i: number): React.ReactNode => {
|
||||||
|
return <div key={`rows-r-${i}`}>{row.elem}</div>;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
id={this.props.id}
|
||||||
|
style={this.props.style}
|
||||||
|
className={this.props.className}
|
||||||
|
>
|
||||||
|
<div className="margin-b-l">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<RiArrowUpDownFill
|
||||||
|
size="3rem"
|
||||||
|
className="black-font margin-r-m"
|
||||||
|
/>,
|
||||||
|
<span>{sortBtns}</span>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([{ flex: "0 0 auto" }, { flex: "0 0 auto" }])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{bodyRows}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,11 @@ import { RiFolder2Fill } from "@react-icons/all-files/ri/RiFolder2Fill";
|
||||||
import { RiArchiveDrawerFill } from "@react-icons/all-files/ri/RiArchiveDrawerFill";
|
import { RiArchiveDrawerFill } from "@react-icons/all-files/ri/RiArchiveDrawerFill";
|
||||||
import { RiFile2Fill } from "@react-icons/all-files/ri/RiFile2Fill";
|
import { RiFile2Fill } from "@react-icons/all-files/ri/RiFile2Fill";
|
||||||
import { RiFileList2Fill } from "@react-icons/all-files/ri/RiFileList2Fill";
|
import { RiFileList2Fill } from "@react-icons/all-files/ri/RiFileList2Fill";
|
||||||
|
import { RiCheckboxFill } from "@react-icons/all-files/ri/RiCheckboxFill";
|
||||||
|
import { RiCheckboxBlankFill } from "@react-icons/all-files/ri/RiCheckboxBlankFill";
|
||||||
|
import { RiInformationFill } from "@react-icons/all-files/ri/RiInformationFill";
|
||||||
|
import { BiTable } from "@react-icons/all-files/bi/BiTable";
|
||||||
|
import { BiListUl } from "@react-icons/all-files/bi/BiListUl";
|
||||||
|
|
||||||
import { alertMsg, confirmMsg } from "../common/env";
|
import { alertMsg, confirmMsg } from "../common/env";
|
||||||
import { getErrMsg } from "../common/utils";
|
import { getErrMsg } from "../common/utils";
|
||||||
|
@ -17,10 +22,13 @@ import { MetadataResp, roleVisitor, roleAdmin } from "../client";
|
||||||
import { Flexbox } from "./layout/flexbox";
|
import { Flexbox } from "./layout/flexbox";
|
||||||
import { Container } from "./layout/container";
|
import { Container } from "./layout/container";
|
||||||
import { Table, Cell, Head } from "./layout/table";
|
import { Table, Cell, Head } from "./layout/table";
|
||||||
|
import { Rows, Row } from "./layout/rows";
|
||||||
import { Up } from "../worker/upload_mgr";
|
import { Up } from "../worker/upload_mgr";
|
||||||
import { UploadEntry, UploadState } from "../worker/interface";
|
import { UploadEntry, UploadState } from "../worker/interface";
|
||||||
import { getIcon } from "./visual/icons";
|
import { getIcon } from "./visual/icons";
|
||||||
|
|
||||||
|
export const filesViewCtrl = "filesView";
|
||||||
|
|
||||||
export interface Item {
|
export interface Item {
|
||||||
name: string;
|
name: string;
|
||||||
size: number;
|
size: number;
|
||||||
|
@ -396,74 +404,10 @@ export class FilesPanel extends React.Component<Props, State, {}> {
|
||||||
this.props.update(updater().updateFilesInfo);
|
this.props.update(updater().updateFilesInfo);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
prepareTable = (
|
||||||
const showOp = this.props.login.userRole === roleVisitor ? "hidden" : "";
|
sortedItems: List<MetadataResp>,
|
||||||
const breadcrumb = this.props.filesInfo.dirPath.map(
|
showOp: string
|
||||||
(pathPart: string, key: number) => {
|
): React.ReactNode => {
|
||||||
return (
|
|
||||||
<button
|
|
||||||
key={pathPart}
|
|
||||||
onClick={() =>
|
|
||||||
this.chdir(this.props.filesInfo.dirPath.slice(0, key + 1))
|
|
||||||
}
|
|
||||||
className="item"
|
|
||||||
>
|
|
||||||
<span className="content">{pathPart}</span>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const ops = (
|
|
||||||
<div id="upload-op">
|
|
||||||
<Flexbox
|
|
||||||
children={List([
|
|
||||||
<div>
|
|
||||||
<button onClick={this.onMkDir} className="float cyan-btn">
|
|
||||||
{this.props.msg.pkg.get("browser.folder.add")}
|
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
onChange={this.onNewFolderNameChange}
|
|
||||||
value={this.state.newFolderName}
|
|
||||||
placeholder={this.props.msg.pkg.get("browser.folder.name")}
|
|
||||||
className="float"
|
|
||||||
/>
|
|
||||||
</div>,
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<button onClick={this.onClickUpload} className="cyan-btn">
|
|
||||||
{this.props.msg.pkg.get("browser.upload")}
|
|
||||||
</button>
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
onChange={this.addUploads}
|
|
||||||
multiple={true}
|
|
||||||
value={this.state.uploadFiles}
|
|
||||||
ref={this.assignInput}
|
|
||||||
className="hidden"
|
|
||||||
/>
|
|
||||||
</div>,
|
|
||||||
])}
|
|
||||||
childrenStyles={List([
|
|
||||||
{ flex: "0 0 70%" },
|
|
||||||
{ flex: "0 0 30%", justifyContent: "flex-end" },
|
|
||||||
])}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const sortedItems = this.props.filesInfo.items.sort(
|
|
||||||
(item1: MetadataResp, item2: MetadataResp) => {
|
|
||||||
if (item1.isDir && !item2.isDir) {
|
|
||||||
return -1;
|
|
||||||
} else if (!item1.isDir && item2.isDir) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const items = sortedItems.map((item: MetadataResp) => {
|
const items = sortedItems.map((item: MetadataResp) => {
|
||||||
const isSelected = this.state.selectedItems.has(item.name);
|
const isSelected = this.state.selectedItems.has(item.name);
|
||||||
const dirPath = this.props.filesInfo.dirPath.join("/");
|
const dirPath = this.props.filesInfo.dirPath.join("/");
|
||||||
|
@ -590,6 +534,243 @@ export class FilesPanel extends React.Component<Props, State, {}> {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Table
|
||||||
|
colStyles={List([
|
||||||
|
{ width: "3rem", paddingRight: "1rem" },
|
||||||
|
{ width: "calc(100% - 12rem)", textAlign: "left" },
|
||||||
|
{ width: "8rem", textAlign: "right" },
|
||||||
|
])}
|
||||||
|
id="item-table"
|
||||||
|
head={tableTitles}
|
||||||
|
foot={List()}
|
||||||
|
rows={items}
|
||||||
|
updateRows={this.updateItems}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
prepareRows = (
|
||||||
|
sortedItems: List<MetadataResp>,
|
||||||
|
showOp: string
|
||||||
|
): React.ReactNode => {
|
||||||
|
const sortKeys = List<string>([
|
||||||
|
this.props.msg.pkg.get("item.type"),
|
||||||
|
this.props.msg.pkg.get("item.name"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const rows = sortedItems.map((item: MetadataResp): Row => {
|
||||||
|
const isSelected = this.state.selectedItems.has(item.name);
|
||||||
|
const dirPath = this.props.filesInfo.dirPath.join("/");
|
||||||
|
const itemPath = dirPath.endsWith("/")
|
||||||
|
? `${dirPath}${item.name}`
|
||||||
|
: `${dirPath}/${item.name}`;
|
||||||
|
|
||||||
|
const selectedIconColor = isSelected ? "cyan0-font" : "grey0-font";
|
||||||
|
const descIconColor = this.state.showDetail.has(item.name)
|
||||||
|
? "cyan0-font"
|
||||||
|
: "grey0-font";
|
||||||
|
const icon = item.isDir ? (
|
||||||
|
<RiFolder2Fill size="1.8rem" className="yellow0-font" />
|
||||||
|
) : (
|
||||||
|
<RiFile2Fill size="1.8rem" className="cyan0-font" />
|
||||||
|
);
|
||||||
|
const fileType = item.isDir
|
||||||
|
? this.props.msg.pkg.get("item.type.folder")
|
||||||
|
: this.props.msg.pkg.get("item.type.file");
|
||||||
|
|
||||||
|
const name = item.isDir ? (
|
||||||
|
<span className="clickable" onClick={() => this.gotoChild(item.name)}>
|
||||||
|
{item.name}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<a
|
||||||
|
className="title-m clickable"
|
||||||
|
href={`/v1/fs/files?fp=${itemPath}`}
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
|
||||||
|
const op = item.isDir ? (
|
||||||
|
<div className={`v-mid item-op ${showOp}`}>
|
||||||
|
<RiCheckboxFill
|
||||||
|
size="1.8rem"
|
||||||
|
className={selectedIconColor}
|
||||||
|
onClick={() => this.select(item.name)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={`v-mid item-op ${showOp}`}>
|
||||||
|
<RiInformationFill
|
||||||
|
size="1.8rem"
|
||||||
|
className={`${descIconColor} margin-r-m`}
|
||||||
|
onClick={() => this.toggleDetail(item.name)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<RiCheckboxFill
|
||||||
|
size="1.8rem"
|
||||||
|
className={selectedIconColor}
|
||||||
|
onClick={() => this.select(item.name)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const pathTitle = this.props.msg.pkg.get("item.path");
|
||||||
|
const modTimeTitle = this.props.msg.pkg.get("item.modTime");
|
||||||
|
const sizeTitle = this.props.msg.pkg.get("item.size");
|
||||||
|
const fileTypeTitle = this.props.msg.pkg.get("item.type");
|
||||||
|
const itemSize = FileSize(item.size, { round: 0 });
|
||||||
|
|
||||||
|
const compact = item.isDir
|
||||||
|
? `${pathTitle}: ${itemPath} | ${modTimeTitle}: ${item.modTime}`
|
||||||
|
: `${pathTitle}: ${itemPath} | ${modTimeTitle}: ${item.modTime} | ${sizeTitle}: ${itemSize} | sha1: ${item.sha1}`;
|
||||||
|
const details = (
|
||||||
|
<div>
|
||||||
|
<div className="card">
|
||||||
|
<span className="title-m black-font">{pathTitle}</span>
|
||||||
|
<span>{itemPath}</span>
|
||||||
|
</div>
|
||||||
|
<div className="card">
|
||||||
|
<span className="title-m black-font">{modTimeTitle}</span>
|
||||||
|
<span>{item.modTime}</span>
|
||||||
|
</div>
|
||||||
|
<div className="card">
|
||||||
|
<span className="title-m black-font">{sizeTitle}</span>
|
||||||
|
<span>{itemSize}</span>
|
||||||
|
</div>
|
||||||
|
<div className="card">
|
||||||
|
<span className="title-m black-font">SHA1</span>
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<input type="text" readOnly={true} value={`${item.sha1}`} />,
|
||||||
|
<button onClick={() => this.generateHash(itemPath)}>
|
||||||
|
{this.props.msg.pkg.get("refresh")}
|
||||||
|
</button>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const desc = this.state.showDetail.has(item.name) ? details : compact;
|
||||||
|
|
||||||
|
const elem = (
|
||||||
|
<div>
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<div>
|
||||||
|
<div className="v-mid">
|
||||||
|
{icon}
|
||||||
|
<span className="margin-l-m desc-l">{`${fileTypeTitle}`}</span>
|
||||||
|
|
||||||
|
<span className="desc-l grey0-font">{`- ${fileType}`}</span>
|
||||||
|
</div>
|
||||||
|
</div>,
|
||||||
|
<div>{op}</div>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
|
||||||
|
/>
|
||||||
|
<div className="name">{name}</div>
|
||||||
|
<div className="desc">{desc}</div>
|
||||||
|
<div className="hr"></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortVals = List<string>([item.isDir ? "d" : "f", itemPath]);
|
||||||
|
return {
|
||||||
|
elem,
|
||||||
|
sortVals,
|
||||||
|
val: item,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Rows
|
||||||
|
sortKeys={sortKeys}
|
||||||
|
rows={List(rows)}
|
||||||
|
updateRows={this.updateItems}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
setView = (opt: string) => {
|
||||||
|
if (opt === "rows" || opt === "table") {
|
||||||
|
updater().setControlOption(filesViewCtrl, opt);
|
||||||
|
this.props.update(updater().updateUI);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO: log error
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const showOp = this.props.login.userRole === roleVisitor ? "hidden" : "";
|
||||||
|
const breadcrumb = this.props.filesInfo.dirPath.map(
|
||||||
|
(pathPart: string, key: number) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={pathPart}
|
||||||
|
onClick={() =>
|
||||||
|
this.chdir(this.props.filesInfo.dirPath.slice(0, key + 1))
|
||||||
|
}
|
||||||
|
className="item"
|
||||||
|
>
|
||||||
|
<span className="content">{pathPart}</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const ops = (
|
||||||
|
<div id="upload-op">
|
||||||
|
<Flexbox
|
||||||
|
children={List([
|
||||||
|
<div>
|
||||||
|
<button onClick={this.onMkDir} className="float cyan-btn">
|
||||||
|
{this.props.msg.pkg.get("browser.folder.add")}
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
onChange={this.onNewFolderNameChange}
|
||||||
|
value={this.state.newFolderName}
|
||||||
|
placeholder={this.props.msg.pkg.get("browser.folder.name")}
|
||||||
|
className="float"
|
||||||
|
/>
|
||||||
|
</div>,
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button onClick={this.onClickUpload} className="cyan-btn">
|
||||||
|
{this.props.msg.pkg.get("browser.upload")}
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
onChange={this.addUploads}
|
||||||
|
multiple={true}
|
||||||
|
value={this.state.uploadFiles}
|
||||||
|
ref={this.assignInput}
|
||||||
|
className="hidden"
|
||||||
|
/>
|
||||||
|
</div>,
|
||||||
|
])}
|
||||||
|
childrenStyles={List([
|
||||||
|
{ flex: "0 0 70%" },
|
||||||
|
{ flex: "0 0 30%", justifyContent: "flex-end" },
|
||||||
|
])}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const viewType = this.props.ui.control.controls.get(filesViewCtrl);
|
||||||
|
const view =
|
||||||
|
viewType === "rows" ? (
|
||||||
|
<div id="item-rows">
|
||||||
|
{this.prepareRows(this.props.filesInfo.items, showOp)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
this.prepareTable(this.props.filesInfo.items, showOp)
|
||||||
|
);
|
||||||
|
|
||||||
const usedSpace = FileSize(parseInt(this.props.login.usedSpace, 10), {
|
const usedSpace = FileSize(parseInt(this.props.login.usedSpace, 10), {
|
||||||
round: 0,
|
round: 0,
|
||||||
});
|
});
|
||||||
|
@ -619,7 +800,7 @@ export class FilesPanel extends React.Component<Props, State, {}> {
|
||||||
this.props.filesInfo.dirPath.join("/")
|
this.props.filesInfo.dirPath.join("/")
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
className="red-btn"
|
className="red-btn left"
|
||||||
>
|
>
|
||||||
{this.props.msg.pkg.get("browser.share.del")}
|
{this.props.msg.pkg.get("browser.share.del")}
|
||||||
</button>
|
</button>
|
||||||
|
@ -627,7 +808,7 @@ export class FilesPanel extends React.Component<Props, State, {}> {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={this.addSharing}
|
onClick={this.addSharing}
|
||||||
className="cyan-btn"
|
className="cyan-btn left"
|
||||||
>
|
>
|
||||||
{this.props.msg.pkg.get("browser.share.add")}
|
{this.props.msg.pkg.get("browser.share.add")}
|
||||||
</button>
|
</button>
|
||||||
|
@ -640,7 +821,7 @@ export class FilesPanel extends React.Component<Props, State, {}> {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => this.delete()}
|
onClick={() => this.delete()}
|
||||||
className="red-btn"
|
className="red-btn left"
|
||||||
>
|
>
|
||||||
{this.props.msg.pkg.get("browser.delete")}
|
{this.props.msg.pkg.get("browser.delete")}
|
||||||
</button>
|
</button>
|
||||||
|
@ -652,15 +833,34 @@ export class FilesPanel extends React.Component<Props, State, {}> {
|
||||||
) : null}
|
) : null}
|
||||||
</span>,
|
</span>,
|
||||||
|
|
||||||
<span>
|
<Flexbox
|
||||||
<span
|
children={List([
|
||||||
id="space-used"
|
<BiListUl
|
||||||
className="desc-m grey0-font"
|
size="1.4rem"
|
||||||
>{`${this.props.msg.pkg.get(
|
className="black-font margin-r-s"
|
||||||
"browser.used"
|
onClick={() => {
|
||||||
)} ${usedSpace} / ${spaceLimit}`}</span>
|
this.setView("rows");
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
<BiTable
|
||||||
|
size="1.4rem"
|
||||||
|
className="black-font margin-r-s"
|
||||||
|
onClick={() => {
|
||||||
|
this.setView("table");
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<span className={`${showOp}`}>
|
||||||
|
<button
|
||||||
|
onClick={() => this.selectAll()}
|
||||||
|
className="select-btn"
|
||||||
|
>
|
||||||
|
{this.props.msg.pkg.get("browser.selectAll")}
|
||||||
|
</button>
|
||||||
</span>,
|
</span>,
|
||||||
])}
|
])}
|
||||||
|
/>,
|
||||||
|
])}
|
||||||
childrenStyles={List([
|
childrenStyles={List([
|
||||||
{ flex: "0 0 auto" },
|
{ flex: "0 0 auto" },
|
||||||
{ flex: "0 0 auto" },
|
{ flex: "0 0 auto" },
|
||||||
|
@ -689,27 +889,19 @@ export class FilesPanel extends React.Component<Props, State, {}> {
|
||||||
/>
|
/>
|
||||||
</span>,
|
</span>,
|
||||||
|
|
||||||
<span className={`${showOp}`}>
|
<span>
|
||||||
<button onClick={() => this.selectAll()} className="select-btn">
|
<span
|
||||||
{this.props.msg.pkg.get("browser.selectAll")}
|
id="space-used"
|
||||||
</button>
|
className="desc-m grey0-font"
|
||||||
|
>{`${this.props.msg.pkg.get(
|
||||||
|
"browser.used"
|
||||||
|
)} ${usedSpace} / ${spaceLimit}`}</span>
|
||||||
</span>,
|
</span>,
|
||||||
])}
|
])}
|
||||||
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
|
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Table
|
{view}
|
||||||
colStyles={List([
|
|
||||||
{ width: "3rem", paddingRight: "1rem" },
|
|
||||||
{ width: "calc(100% - 12rem)", textAlign: "left" },
|
|
||||||
{ width: "8rem", textAlign: "right" },
|
|
||||||
])}
|
|
||||||
id="item-table"
|
|
||||||
head={tableTitles}
|
|
||||||
foot={List()}
|
|
||||||
rows={items}
|
|
||||||
updateRows={this.updateItems}
|
|
||||||
/>
|
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -313,15 +313,37 @@ export class Updater {
|
||||||
const isAuthed = this.props.login.authed;
|
const isAuthed = this.props.login.authed;
|
||||||
const isSharing =
|
const isSharing =
|
||||||
this.props.ui.control.controls.get(sharingCtrl) === ctrlOn;
|
this.props.ui.control.controls.get(sharingCtrl) === ctrlOn;
|
||||||
|
const leftControls = this.props.ui.control.controls.filter(
|
||||||
|
(_: string, key: string): boolean => {
|
||||||
|
return (
|
||||||
|
key !== panelTabs &&
|
||||||
|
key !== settingsDialogCtrl &&
|
||||||
|
key !== settingsTabsCtrl &&
|
||||||
|
key !== sharingCtrl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const leftOpts = this.props.ui.control.options.filter(
|
||||||
|
(_: Set<string>, key: string): boolean => {
|
||||||
|
return (
|
||||||
|
key !== panelTabs &&
|
||||||
|
key !== settingsDialogCtrl &&
|
||||||
|
key !== settingsTabsCtrl &&
|
||||||
|
key !== sharingCtrl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let newControls: Map<string, string> = undefined;
|
||||||
|
let newOptions: Map<string, Set<string>> = undefined;
|
||||||
if (isAuthed) {
|
if (isAuthed) {
|
||||||
this.props.ui.control.controls = Map<string, string>({
|
newControls = Map<string, string>({
|
||||||
[panelTabs]: "filesPanel",
|
[panelTabs]: "filesPanel",
|
||||||
[settingsDialogCtrl]: ctrlOff,
|
[settingsDialogCtrl]: ctrlOff,
|
||||||
[settingsTabsCtrl]: "preferencePane",
|
[settingsTabsCtrl]: "preferencePane",
|
||||||
[sharingCtrl]: isSharing ? ctrlOn : ctrlOff,
|
[sharingCtrl]: isSharing ? ctrlOn : ctrlOff,
|
||||||
});
|
});
|
||||||
this.props.ui.control.options = Map<string, Set<string>>({
|
newOptions = Map<string, Set<string>>({
|
||||||
[panelTabs]: Set<string>([
|
[panelTabs]: Set<string>([
|
||||||
"filesPanel",
|
"filesPanel",
|
||||||
"uploadingsPanel",
|
"uploadingsPanel",
|
||||||
|
@ -333,33 +355,33 @@ export class Updater {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.props.login.userRole == roleAdmin) {
|
if (this.props.login.userRole == roleAdmin) {
|
||||||
this.props.ui.control.options = this.props.ui.control.options.set(
|
newOptions = newOptions.set(
|
||||||
settingsTabsCtrl,
|
settingsTabsCtrl,
|
||||||
Set<string>(["preferencePane", "managementPane"])
|
Set<string>(["preferencePane", "managementPane"])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isSharing) {
|
if (isSharing) {
|
||||||
this.props.ui.control.controls = Map<string, string>({
|
newControls = Map<string, string>({
|
||||||
[panelTabs]: "filesPanel",
|
[panelTabs]: "filesPanel",
|
||||||
[settingsDialogCtrl]: ctrlHidden,
|
[settingsDialogCtrl]: ctrlHidden,
|
||||||
[settingsTabsCtrl]: ctrlHidden,
|
[settingsTabsCtrl]: ctrlHidden,
|
||||||
[sharingCtrl]: ctrlOn,
|
[sharingCtrl]: ctrlOn,
|
||||||
});
|
});
|
||||||
this.props.ui.control.options = Map<string, Set<string>>({
|
newOptions = Map<string, Set<string>>({
|
||||||
[panelTabs]: Set<string>(["filesPanel"]),
|
[panelTabs]: Set<string>(["filesPanel"]),
|
||||||
[settingsDialogCtrl]: Set<string>([ctrlHidden]),
|
[settingsDialogCtrl]: Set<string>([ctrlHidden]),
|
||||||
[settingsTabsCtrl]: Set<string>([ctrlHidden]),
|
[settingsTabsCtrl]: Set<string>([ctrlHidden]),
|
||||||
[sharingCtrl]: Set<string>([ctrlOn]),
|
[sharingCtrl]: Set<string>([ctrlOn]),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.props.ui.control.controls = Map<string, string>({
|
newControls = Map<string, string>({
|
||||||
[panelTabs]: ctrlHidden,
|
[panelTabs]: ctrlHidden,
|
||||||
[settingsDialogCtrl]: ctrlHidden,
|
[settingsDialogCtrl]: ctrlHidden,
|
||||||
[settingsTabsCtrl]: ctrlHidden,
|
[settingsTabsCtrl]: ctrlHidden,
|
||||||
[sharingCtrl]: ctrlOff,
|
[sharingCtrl]: ctrlOff,
|
||||||
});
|
});
|
||||||
this.props.ui.control.options = Map<string, Set<string>>({
|
newOptions = Map<string, Set<string>>({
|
||||||
[panelTabs]: Set<string>([ctrlHidden]),
|
[panelTabs]: Set<string>([ctrlHidden]),
|
||||||
[settingsDialogCtrl]: Set<string>([ctrlHidden]),
|
[settingsDialogCtrl]: Set<string>([ctrlHidden]),
|
||||||
[settingsTabsCtrl]: Set<string>([ctrlHidden]),
|
[settingsTabsCtrl]: Set<string>([ctrlHidden]),
|
||||||
|
@ -367,6 +389,9 @@ export class Updater {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.props.ui.control.controls = newControls.merge(leftControls);
|
||||||
|
this.props.ui.control.options = newOptions.merge(leftOpts);
|
||||||
};
|
};
|
||||||
|
|
||||||
initStateForVisitor = async (): Promise<any> => {
|
initStateForVisitor = async (): Promise<any> => {
|
||||||
|
|
|
@ -14,8 +14,9 @@ import { RiInformationFill } from "@react-icons/all-files/ri/RiInformationFill";
|
||||||
import { RiDeleteBin2Fill } from "@react-icons/all-files/ri/RiDeleteBin2Fill";
|
import { RiDeleteBin2Fill } from "@react-icons/all-files/ri/RiDeleteBin2Fill";
|
||||||
import { RiArchiveDrawerFill } from "@react-icons/all-files/ri/RiArchiveDrawerFill";
|
import { RiArchiveDrawerFill } from "@react-icons/all-files/ri/RiArchiveDrawerFill";
|
||||||
import { RiFileList2Fill } from "@react-icons/all-files/ri/RiFileList2Fill";
|
import { RiFileList2Fill } from "@react-icons/all-files/ri/RiFileList2Fill";
|
||||||
|
import { RiArrowUpDownFill } from "@react-icons/all-files/ri/RiArrowUpDownFill";
|
||||||
|
import { BiTable } from "@react-icons/all-files/bi/BiTable";
|
||||||
|
import { BiListUl } from "@react-icons/all-files/bi/BiListUl";
|
||||||
|
|
||||||
import { colorClass } from "./colors";
|
import { colorClass } from "./colors";
|
||||||
|
|
||||||
|
@ -38,6 +39,9 @@ const icons = Map<string, IconType>({
|
||||||
RiInformationFill: RiInformationFill,
|
RiInformationFill: RiInformationFill,
|
||||||
RiDeleteBin2Fill: RiDeleteBin2Fill,
|
RiDeleteBin2Fill: RiDeleteBin2Fill,
|
||||||
RiArchiveDrawerFill: RiArchiveDrawerFill,
|
RiArchiveDrawerFill: RiArchiveDrawerFill,
|
||||||
|
RiArrowUpDownFill: RiArrowUpDownFill,
|
||||||
|
BiTable: BiTable,
|
||||||
|
BiListUl: BiListUl,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function getIconWithProps(
|
export function getIconWithProps(
|
||||||
|
|
|
@ -118,4 +118,11 @@ export const msgs: Map<string, string> = Map({
|
||||||
"err.server": "The operation failed in the server",
|
"err.server": "The operation failed in the server",
|
||||||
"err.script.cors": "script error with CORS",
|
"err.script.cors": "script error with CORS",
|
||||||
"err.unknown": "unknown error",
|
"err.unknown": "unknown error",
|
||||||
|
"item.type": "Item Type",
|
||||||
|
"item.type.folder": "Folder",
|
||||||
|
"item.type.file": "File",
|
||||||
|
"item.name": "Item Name",
|
||||||
|
"item.path": "Path",
|
||||||
|
"item.modTime": "Mod Time",
|
||||||
|
"item.size": "Size",
|
||||||
});
|
});
|
||||||
|
|
|
@ -115,4 +115,11 @@ export const msgs: Map<string, string> = Map({
|
||||||
"err.server": "服务器端操作失败",
|
"err.server": "服务器端操作失败",
|
||||||
"err.script.cors": "跨域脚本错误",
|
"err.script.cors": "跨域脚本错误",
|
||||||
"err.unknown": "未知错误",
|
"err.unknown": "未知错误",
|
||||||
|
"item.type": "类型",
|
||||||
|
"item.type.folder": "文件夹",
|
||||||
|
"item.type.file": "文件",
|
||||||
|
"item.name": "名称",
|
||||||
|
"item.path": "路径",
|
||||||
|
"item.modTime": "修改时间",
|
||||||
|
"item.size": "大小",
|
||||||
});
|
});
|
||||||
|
|
58
yarn.lock
58
yarn.lock
|
@ -1149,9 +1149,9 @@
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
|
|
||||||
"@polka/url@^1.0.0-next.20":
|
"@polka/url@^1.0.0-next.20":
|
||||||
version "1.0.0-next.20"
|
version "1.0.0-next.21"
|
||||||
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.20.tgz#111b5db0f501aa89b05076fa31f0ea0e0c292cd3"
|
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
|
||||||
integrity sha512-88p7+M0QGxKpmnkfXjS4V26AnoC/eiqZutE8GLdaI5X12NY75bXSdTY9NkmYb2Xyk1O+MmkuO6Frmsj84V6I8Q==
|
integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==
|
||||||
|
|
||||||
"@react-icons/all-files@^4.1.0":
|
"@react-icons/all-files@^4.1.0":
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
|
@ -1615,7 +1615,12 @@ acorn@^7.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||||
|
|
||||||
acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1:
|
acorn@^8.0.4:
|
||||||
|
version "8.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895"
|
||||||
|
integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==
|
||||||
|
|
||||||
|
acorn@^8.2.4, acorn@^8.4.1:
|
||||||
version "8.5.0"
|
version "8.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
|
||||||
integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
|
integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
|
||||||
|
@ -2063,12 +2068,7 @@ commander@^4.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
|
||||||
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
|
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
|
||||||
|
|
||||||
commander@^6.2.0:
|
commander@^7.0.0, commander@^7.2.0:
|
||||||
version "6.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
|
||||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
|
||||||
|
|
||||||
commander@^7.0.0:
|
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
|
||||||
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
||||||
|
@ -3667,11 +3667,6 @@ mime-types@^2.1.12, mime-types@^2.1.27:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-db "1.49.0"
|
mime-db "1.49.0"
|
||||||
|
|
||||||
mime@^2.3.1:
|
|
||||||
version "2.5.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe"
|
|
||||||
integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==
|
|
||||||
|
|
||||||
mimic-fn@^2.1.0:
|
mimic-fn@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||||
|
@ -3696,6 +3691,11 @@ mkdirp@^0.5.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.5"
|
minimist "^1.2.5"
|
||||||
|
|
||||||
|
mrmime@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.0.tgz#14d387f0585a5233d291baba339b063752a2398b"
|
||||||
|
integrity sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ==
|
||||||
|
|
||||||
ms@2.1.2:
|
ms@2.1.2:
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
|
@ -4062,6 +4062,11 @@ react-dom@^16.8.6:
|
||||||
prop-types "^15.6.2"
|
prop-types "^15.6.2"
|
||||||
scheduler "^0.19.1"
|
scheduler "^0.19.1"
|
||||||
|
|
||||||
|
react-icons@4.3.1:
|
||||||
|
version "4.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.3.1.tgz#2fa92aebbbc71f43d2db2ed1aed07361124e91ca"
|
||||||
|
integrity sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==
|
||||||
|
|
||||||
react-is@^16.8.1:
|
react-is@^16.8.1:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
|
@ -4336,12 +4341,12 @@ signal-exit@^3.0.2, signal-exit@^3.0.3:
|
||||||
integrity sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==
|
integrity sha512-rqYhcAnZ6d/vTPGghdrw7iumdcbXpsk1b8IG/rz+VWV51DM0p7XCtMoJ3qhPLIbp3tvyt3pKRbaaEMZYpHto8Q==
|
||||||
|
|
||||||
sirv@^1.0.7:
|
sirv@^1.0.7:
|
||||||
version "1.0.17"
|
version "1.0.19"
|
||||||
resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.17.tgz#86e2c63c612da5a1dace1c16c46f524aaa26ac45"
|
resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49"
|
||||||
integrity sha512-qx9go5yraB7ekT7bCMqUHJ5jEaOC/GXBxUWv+jeWnb7WzHUFdcQPGWk7YmAwFBaQBrogpuSqd/azbC2lZRqqmw==
|
integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@polka/url" "^1.0.0-next.20"
|
"@polka/url" "^1.0.0-next.20"
|
||||||
mime "^2.3.1"
|
mrmime "^1.0.0"
|
||||||
totalist "^1.0.0"
|
totalist "^1.0.0"
|
||||||
|
|
||||||
sisteransi@^1.0.5:
|
sisteransi@^1.0.5:
|
||||||
|
@ -4855,14 +4860,14 @@ webidl-conversions@^6.1.0:
|
||||||
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
|
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
|
||||||
|
|
||||||
webpack-bundle-analyzer@^4.4.2:
|
webpack-bundle-analyzer@^4.4.2:
|
||||||
version "4.4.2"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.2.tgz#39898cf6200178240910d629705f0f3493f7d666"
|
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
|
||||||
integrity sha512-PIagMYhlEzFfhMYOzs5gFT55DkUdkyrJi/SxJp8EF3YMWhS+T9vvs2EoTetpk5qb6VsCq02eXTlRDOydRhDFAQ==
|
integrity sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn "^8.0.4"
|
acorn "^8.0.4"
|
||||||
acorn-walk "^8.0.0"
|
acorn-walk "^8.0.0"
|
||||||
chalk "^4.1.0"
|
chalk "^4.1.0"
|
||||||
commander "^6.2.0"
|
commander "^7.2.0"
|
||||||
gzip-size "^6.0.0"
|
gzip-size "^6.0.0"
|
||||||
lodash "^4.17.20"
|
lodash "^4.17.20"
|
||||||
opener "^1.5.2"
|
opener "^1.5.2"
|
||||||
|
@ -5031,7 +5036,12 @@ write-file-atomic@^3.0.0:
|
||||||
signal-exit "^3.0.2"
|
signal-exit "^3.0.2"
|
||||||
typedarray-to-buffer "^3.1.5"
|
typedarray-to-buffer "^3.1.5"
|
||||||
|
|
||||||
ws@^7.3.1, ws@^7.4.6:
|
ws@^7.3.1:
|
||||||
|
version "7.5.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b"
|
||||||
|
integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==
|
||||||
|
|
||||||
|
ws@^7.4.6:
|
||||||
version "7.5.5"
|
version "7.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881"
|
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881"
|
||||||
integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==
|
integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue