diff --git a/src/client/web/src/components/layout/table.tsx b/src/client/web/src/components/layout/table.tsx index e5c50c9..4480a87 100644 --- a/src/client/web/src/components/layout/table.tsx +++ b/src/client/web/src/components/layout/table.tsx @@ -1,60 +1,201 @@ import * as React from "react"; import { List } from "immutable"; +import { updater } from "../state_updater"; + +export interface Cell { + elem: React.ReactNode; + val: string; // original cell value + // sortVal: string; // this value is for sorting +} + +export interface Row { + cells: List; + val: Object; // original object value +} + +export interface Head { + elem: React.ReactNode; + sortable: boolean; +} + export interface Props { - head: List; - rows: List>; + head: List; + rows: List; foot: List; colStyles?: List; id?: string; style?: React.CSSProperties; className?: string; + originalVals?: List; + updateRows?: (rows: Object) => void; } -export const Table = (props: Props) => { - const headCols = props.head.map( - (elem: React.ReactNode, i: number): React.ReactNode => { - const style = props.colStyles != null ? props.colStyles.get(i) : {}; - return ( - - {elem} - - ); - } - ); - const bodyRows = props.rows.map( - (row: List, i: number): React.ReactNode => { - const tds = row.map((elem: React.ReactNode, j: number) => { - const style = props.colStyles != null ? props.colStyles.get(j) : {}; - return ( - - {elem} - - ); - }); - return {tds}; - } - ); - const footCols = props.foot.map( - (elem: React.ReactNode, i: number): React.ReactNode => { - const style = props.colStyles != null ? props.colStyles.get(i) : {}; - return ( - - {elem} - - ); - } - ); +export interface State { + orders: List; // asc = true, desc = false +} - return ( - - - {headCols} - - {bodyRows} - - {footCols} - -
- ); -}; +export class Table extends React.Component { + constructor(p: Props) { + super(p); + this.state = { + // TODO: if the size of column increases + // state will be out of sync with props + // so this will not work + orders: p.head.map((_, i: number) => { + return i === 0; // order by the first column + }), + }; + } + + sortRows = (colIndex: number) => { + if (this.props.updateRows == null) { + return; + } + const headCell = this.props.head.get(colIndex); + if (headCell == null || !headCell.sortable) { + return; + } + const currentOrder = this.state.orders.get(colIndex); + if (currentOrder == null) { + return; + } + + const sortedRows = this.props.rows.sort((row1: Row, row2: Row) => { + const cell1 = row1.cells.get(colIndex); + const cell2 = row2.cells.get(colIndex); + + if (cell1 == null || cell2 == null) { + // keep current order + return currentOrder ? -1 : 1; + } else if (cell1.val < cell2.val) { + return -1; + } else if (cell1.val == cell2.val) { + return 0; + } else { + return currentOrder ? 1 : -1; + } + }); + + const sortedItems = sortedRows.map((row: Row): Object => { + return row.val; + }); + + const newOrders = this.state.orders.set(colIndex, !currentOrder); + this.setState({ orders: newOrders }); + this.props.updateRows(sortedItems); + }; + + render() { + const headCols = this.props.head.map( + (head: Head, i: number): React.ReactNode => { + const style = + this.props.colStyles != null ? this.props.colStyles.get(i) : {}; + + return ( + { + this.sortRows(i); + }} + > + {head.elem} + + ); + } + ); + + const bodyRows = this.props.rows.map( + (row: Row, i: number): React.ReactNode => { + const tds = row.cells.map((cell: Cell, j: number) => { + const style = + this.props.colStyles != null ? this.props.colStyles.get(j) : {}; + return ( + + {cell.elem} + + ); + }); + return {tds}; + } + ); + + const footCols = this.props.foot.map( + (elem: React.ReactNode, i: number): React.ReactNode => { + const style = + this.props.colStyles != null ? this.props.colStyles.get(i) : {}; + return ( + + {elem} + + ); + } + ); + + return ( + + + {headCols} + + {bodyRows} + + {footCols} + +
+ ); + } +} + +// export const Table = (props: Props) => { +// const headCols = props.head.map((head: Head, i: number): React.ReactNode => { +// const style = props.colStyles != null ? props.colStyles.get(i) : {}; +// return ( +// +// {head.elem} +// +// ); +// }); + +// const bodyRows = props.rows.map( +// (row: List, i: number): React.ReactNode => { +// const tds = row.map((cell: Cell, j: number) => { +// const style = props.colStyles != null ? props.colStyles.get(j) : {}; +// return ( +// +// {cell.elem} +// +// ); +// }); +// return {tds}; +// } +// ); + +// const footCols = props.foot.map( +// (elem: React.ReactNode, i: number): React.ReactNode => { +// const style = props.colStyles != null ? props.colStyles.get(i) : {}; +// return ( +// +// {elem} +// +// ); +// } +// ); + +// return ( +// +// +// {headCols} +// +// {bodyRows} +// +// {footCols} +// +//
+// ); +// }; diff --git a/src/client/web/src/components/panel_files.tsx b/src/client/web/src/components/panel_files.tsx index 8f0d030..347cd85 100644 --- a/src/client/web/src/components/panel_files.tsx +++ b/src/client/web/src/components/panel_files.tsx @@ -15,7 +15,7 @@ import { LoginProps } from "./pane_login"; import { MetadataResp, roleVisitor, roleAdmin } from "../client"; import { Flexbox } from "./layout/flexbox"; import { Container } from "./layout/container"; -import { Table } from "./layout/table"; +import { Table, Cell, Head } from "./layout/table"; import { Up } from "../worker/upload_mgr"; import { UploadEntry, UploadState } from "../worker/interface"; import { getIcon } from "./visual/icons"; @@ -332,6 +332,12 @@ export class FilesPanel extends React.Component { }); }; + updateItems = (items: Object) => { + const metadataResps = items as List; + updater().updateItems(metadataResps); + this.props.update(updater().updateFilesInfo); + }; + render() { const showOp = this.props.login.userRole === roleVisitor ? "hidden" : ""; const breadcrumb = this.props.filesInfo.dirPath.map( @@ -381,18 +387,18 @@ export class FilesPanel extends React.Component { ); - 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 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 = this.props.filesInfo.items.map((item: MetadataResp) => { const isSelected = this.state.selectedItems.has(item.name); const dirPath = this.props.filesInfo.dirPath.join("/"); const itemPath = dirPath.endsWith("/") @@ -412,7 +418,10 @@ export class FilesPanel extends React.Component { const content = item.isDir ? (
-
this.gotoChild(item.name)}> +
this.gotoChild(item.name)} + > {item.name}
@@ -486,9 +495,35 @@ export class FilesPanel extends React.Component {
); - return List([icon, content, op]); + return { + val: item, + cells: List([ + { elem: icon, val: item.isDir ? "d" : "f" }, + { elem: content, val: itemPath }, + { elem: op, val: "" }, + ]), + }; }); + const tableTitles = List([ + { + elem: ( +
+ +
+ ), + sortable: true, + }, + { + elem:
Name
, + sortable: true, + }, + { + elem:
Action
, + sortable: false, + }, + ]); + const usedSpace = FileSize(parseInt(this.props.login.usedSpace, 10), { round: 0, }); @@ -499,14 +534,6 @@ export class FilesPanel extends React.Component { } ); - const tableTitles = List([ -
- -
, -
Name
, -
Action
, - ]); - const itemListPane = (
@@ -615,6 +642,7 @@ export class FilesPanel extends React.Component { head={tableTitles} foot={List()} rows={items} + updateRows={this.updateItems} />
diff --git a/src/client/web/src/components/state_updater.ts b/src/client/web/src/components/state_updater.ts index 9d198a6..2adef0a 100644 --- a/src/client/web/src/components/state_updater.ts +++ b/src/client/web/src/components/state_updater.ts @@ -247,6 +247,10 @@ export class Updater { return false; }; + updateItems = (items: List) => { + this.props.filesInfo.items = items; + }; + moveHere = async ( srcDir: string, dstDir: string,