Merge "Web Client context menu item display"
[ccsdk/features.git] / sdnr / wt / odlux / framework / src / components / material-table / index.tsx
index 9155f38..c1a5005 100644 (file)
  * ============LICENSE_END==========================================================================
  */
 import * as React from 'react';
-import { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles';
+import { Theme } from '@mui/material/styles';
 
-import Table from '@material-ui/core/Table';
-import TableBody from '@material-ui/core/TableBody';
-import TableCell from '@material-ui/core/TableCell';
-import TableContainer from '@material-ui/core/TableContainer';
-import TablePagination from '@material-ui/core/TablePagination';
-import TableRow from '@material-ui/core/TableRow';
-import Paper from '@material-ui/core/Paper';
-import Checkbox from '@material-ui/core/Checkbox';
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+
+import Table from '@mui/material/Table';
+import TableBody from '@mui/material/TableBody';
+import TableCell from '@mui/material/TableCell';
+import TableContainer from '@mui/material/TableContainer';
+import TablePagination from '@mui/material/TablePagination';
+import TableRow from '@mui/material/TableRow';
+import Paper from '@mui/material/Paper';
+import Checkbox from '@mui/material/Checkbox';
 
 import { TableToolbar } from './tableToolbar';
 import { EnhancedTableHead } from './tableHead';
 import { EnhancedTableFilter } from './tableFilter';
 
 import { ColumnModel, ColumnType } from './columnModel';
-import { Omit, Menu, makeStyles } from '@material-ui/core';
+import { Menu, Typography } from '@mui/material';
+import { DistributiveOmit } from '@mui/types';
+
+import makeStyles from '@mui/styles/makeStyles';
 
-import { SvgIconProps } from '@material-ui/core/SvgIcon/SvgIcon';
+import { SvgIconProps } from '@mui/material/SvgIcon';
 
-import { DividerTypeMap } from '@material-ui/core/Divider';
-import { MenuItemProps } from '@material-ui/core/MenuItem';
-import { flexbox } from '@material-ui/system';
+import { DividerTypeMap } from '@mui/material/Divider';
+import { MenuItemProps } from '@mui/material/MenuItem';
+import { flexbox } from '@mui/system';
 import { RowDisabled } from './utilities';
+import { toAriaLabel } from '../../utilities/yangHelper';
 export { ColumnModel, ColumnType } from './columnModel';
 
 type propType = string | number | null | undefined | (string | number)[];
@@ -50,7 +58,7 @@ export type DataCallback<TData = dataType> = (page?: number, rowsPerPage?: numbe
 
 function regExpEscape(s: string) {
   return s.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&');
-};
+}
 
 function wildcardCheck(input: string, pattern: string) {
    if (!pattern) return true; 
@@ -60,7 +68,7 @@ function wildcardCheck(input: string, pattern: string) {
      (!pattern.endsWith('*') ? '$' : '')
    );
    return input.match(regex) !== null && input.match(regex)!.length >= 1;
-};
+}
 
 function desc(a: dataType, b: dataType, orderBy: string) {
   if ((b[orderBy] || "") < (a[orderBy] || "")) {
@@ -100,7 +108,8 @@ const styles = (theme: Theme) => createStyles({
     flex: "1 1 100%"
   },
   pagination: {
-    overflow: "hidden"
+    overflow: "hidden",
+    minHeight: "52px"
   }
 });
 
@@ -142,18 +151,23 @@ export type MaterialTableComponentState<TData = {}> = {
   rowsPerPage: number;
   loading: boolean;
   showFilter: boolean;
+  hiddenColumns: string[];
   filter: { [property: string]: string };
 };
 
 export type TableApi = { forceRefresh?: () => Promise<void> };
 
-type MaterialTableComponentBaseProps<TData> = WithStyles<typeof styles> & {
+type MaterialTableComponentBaseProps<TData> = WithStyles<typeof styles>  & {
   className?: string;
   columns: ColumnModel<TData>[];
   idProperty: keyof TData | ((data: TData) => React.Key);
-  tableId?: string;
+  
+  //Note: used to save settings as well. Must be unique across apps. Null tableIds will not get saved to the settings
+  tableId: string | null;
+  isPopup?: boolean;
   title?: string;
   stickyHeader?: boolean;
+  allowHtmlHeader?: boolean;
   defaultSortOrder?: 'asc' | 'desc';
   defaultSortColumn?: keyof TData;
   enableSelection?: boolean;
@@ -172,6 +186,8 @@ type MaterialTableComponentPropsWithExternalState<TData = {}> = MaterialTableCom
   onHandleChangePage: (page: number) => void;
   onHandleChangeRowsPerPage: (rowsPerPage: number | null) => void;
   onHandleRequestSort: (property: string) => void;
+  onHideColumns : (columnNames: string[]) => void
+  onShowColumns:  (columnNames: string[]) => void
 };
 
 type MaterialTableComponentProps<TData = {}> =
@@ -193,13 +209,18 @@ function isMaterialTableComponentPropsWithRowsAndRequestData(props: MaterialTabl
     propsWithExternalState.onHandleChangePage instanceof Function ||
     propsWithExternalState.onHandleChangeRowsPerPage instanceof Function ||
     propsWithExternalState.onToggleFilter instanceof Function ||
+    propsWithExternalState.onHideColumns instanceof Function ||
     propsWithExternalState.onHandleRequestSort instanceof Function
 }
 
+// get settings in here!
+
+
 class MaterialTableComponent<TData extends {} = {}> extends React.Component<MaterialTableComponentProps, MaterialTableComponentState & { contextMenuInfo: { index: number; mouseX?: number; mouseY?: number }; }> {
 
   constructor(props: MaterialTableComponentProps) {
     super(props);
+    
 
     const page = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.page : 0;
     const rowsPerPage = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.rowsPerPage || 10 : 10;
@@ -214,6 +235,7 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
       selected: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.selected : null,
       rows: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) || [],
       total: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.length || 0,
+      hiddenColumns: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) && this.props.hiddenColumns || [],
       page,
       rowsPerPage,
     };
@@ -227,18 +249,27 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
     }
   }
   render(): JSX.Element {
-    const { classes, columns } = this.props;
+    const { classes, columns, allowHtmlHeader } = this.props;
     const { rows, total: rowCount, order, orderBy, selected, rowsPerPage, page, showFilter, filter } = this.state;
     const emptyRows = rowsPerPage - Math.min(rowsPerPage, rowCount - page * rowsPerPage);
     const getId = typeof this.props.idProperty !== "function" ? (data: TData) => ((data as { [key: string]: any })[this.props.idProperty as any as string] as string | number) : this.props.idProperty;
     const toggleFilter = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.onToggleFilter : () => { !this.props.disableFilter && this.setState({ showFilter: !showFilter }, this.update) }
+
+    const hideColumns = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.onHideColumns : (data: string[]) => { const newArray = [...new Set([...this.state.hiddenColumns, ...data])]; this.setState({hiddenColumns:newArray}); }
+    const showColumns = isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.onShowColumns : (data: string[]) => { const newArray = this.state.hiddenColumns.filter(el=> !data.includes(el));   this.setState({hiddenColumns:newArray}); }
+
+    const allColumnsHidden = this.props.columns.length === this.state.hiddenColumns.length;
     return (
       <Paper className={this.props.className ? `${classes.root} ${this.props.className}` : classes.root}>
         <TableContainer className={classes.container}>
           <TableToolbar tableId={this.props.tableId} numSelected={selected && selected.length} title={this.props.title} customActionButtons={this.props.customActionButtons} onExportToCsv={this.exportToCsv}
-            onToggleFilter={toggleFilter} />
-          <Table aria-label={this.props.tableId ? this.props.tableId : 'tableTitle'} stickyHeader={this.props.stickyHeader || false} >
+            onToggleFilter={toggleFilter}
+            columns={columns}
+            onHideColumns={hideColumns}
+            onShowColumns={showColumns} />
+          <Table padding="normal" aria-label={this.props.tableId ? this.props.tableId : 'tableTitle'} stickyHeader={this.props.stickyHeader || false} >
             <EnhancedTableHead
+              allowHtmlHeader={allowHtmlHeader || false}
               columns={columns}
               numSelected={selected && selected.length}
               order={order}
@@ -247,10 +278,14 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
               onRequestSort={this.onHandleRequestSort}
               rowCount={rows.length}
               enableSelection={this.props.enableSelection}
+              hiddenColumns={this.state.hiddenColumns}
             />
             <TableBody>
-              {showFilter && <EnhancedTableFilter columns={columns} filter={filter} onFilterChanged={this.onFilterChanged} enableSelection={this.props.enableSelection} /> || null}
-              {rows // may need ordering here
+              {showFilter && <EnhancedTableFilter columns={columns} hiddenColumns={this.state.hiddenColumns} filter={filter} onFilterChanged={this.onFilterChanged} enableSelection={this.props.enableSelection} /> || null}
+              
+              {allColumnsHidden ? <Typography variant="body1" textAlign="center">All columns of this table are hidden.</Typography> :
+              
+              rows // may need ordering here
                 .map((entry: TData & { [RowDisabled]?: boolean, [kex: string]: any }, index) => {
                   const entryId = getId(entry);
                   const contextMenu = (this.props.createContextMenu && this.state.contextMenuInfo.index === index && this.props.createContextMenu(entry)) || null;
@@ -285,16 +320,18 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
                     >
                       {this.props.enableSelection
                         ? <TableCell padding="checkbox" style={{ width: "50px", color:  entry[RowDisabled] || false ? "inherit" : undefined } }>
-                          <Checkbox checked={isSelected} />
+                          <Checkbox color='secondary' checked={isSelected} />
                         </TableCell>
                         : null
                       }
                       {
+                        
                         this.props.columns.map(
                           col => {
                             const style = col.width ? { width: col.width } : {};
-                            return (
-                              <TableCell style={ entry[RowDisabled] || false ? { ...style, color: "inherit"  } : style } aria-label={col.title? col.title.toLowerCase().replace(/\s/g, "-") : col.property.toLowerCase().replace(/\s/g, "-")} key={col.property} align={col.type === ColumnType.numeric && !col.align ? "right" : col.align} >
+                            const tableCell = (
+
+                              <TableCell style={ entry[RowDisabled] || false ? { ...style, color: "inherit"  } : style } aria-label={col.title? toAriaLabel(col.title) : toAriaLabel(col.property)} key={col.property} align={col.type === ColumnType.numeric && !col.align ? "right" : col.align} >
                                 {col.type === ColumnType.custom && col.customControl
                                   ? <col.customControl className={col.className} style={col.style} rowData={entry} />
                                   : col.type === ColumnType.boolean
@@ -303,6 +340,10 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
                                 }
                               </TableCell>
                             );
+                            
+                            //show column if...
+                            const showColumn = !this.state.hiddenColumns.includes(col.property);
+                            return showColumn && tableCell
                           }
                         )
                       }
@@ -327,21 +368,22 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
           count={rowCount}
           rowsPerPage={rowsPerPage}
           page={page}
-          aria-label="table-pagination-footer"
+          aria-label={this.props.isPopup ? "popup-table-pagination-footer" : "table-pagination-footer" }
           backIconButtonProps={{
-            'aria-label': 'previous-page',
+            'aria-label': this.props.isPopup ? 'popup-previous-page' : 'previous-page',
           }}
           nextIconButtonProps={{
-            'aria-label': 'next-page',
+            'aria-label': this.props.isPopup ? 'popup-next-page': 'next-page',
           }}
-          onChangePage={this.onHandleChangePage}
-          onChangeRowsPerPage={this.onHandleChangeRowsPerPage}
+          onPageChange={this.onHandleChangePage}
+          onRowsPerPageChange={this.onHandleChangeRowsPerPage}
         />
       </Paper>
     );
   }
 
   static getDerivedStateFromProps(props: MaterialTableComponentProps, state: MaterialTableComponentState & { _rawRows: {}[] }): MaterialTableComponentState & { _rawRows: {}[] } {
+   
     if (isMaterialTableComponentPropsWithRowsAndRequestData(props)) {
       return {
         ...state,
@@ -353,6 +395,7 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
         loading: props.loading,
         showFilter: props.showFilter,
         page: props.page,
+        hiddenColumns: props.hiddenColumns,
         rowsPerPage: props.rowsPerPage
       }
     } else if (isMaterialTableComponentPropsWithRows(props) && props.asynchronus && state._rawRows !== props.rows) {
@@ -368,7 +411,7 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
 
   private static updateRows(props: MaterialTableComponentPropsWithRows, state: MaterialTableComponentState): { rows: {}[], total: number, page: number } {
 
-    let data = [...props.rows as dataType[] || []];
+    let data = [...(props.rows as dataType[] || [])];
     const columns = props.columns;
 
     const { page, rowsPerPage, order, orderBy, filter } = state;
@@ -407,13 +450,13 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
                   if (filterExpressionAsString.length === 0 || isNaN(valueAsNumber)) return true;
                   
                   if (filterExpressionAsString.startsWith('>=')) {
-                    return valueAsNumber >= Number(filterExpressionAsString.substr(2).trim());
+                    return valueAsNumber >= Number(filterExpressionAsString.substring(2).trim());
                   } else if (filterExpressionAsString.startsWith('<=')) {
-                    return valueAsNumber <= Number(filterExpressionAsString.substr(2).trim());
+                    return valueAsNumber <= Number(filterExpressionAsString.substring(2).trim());
                   } else if (filterExpressionAsString.startsWith('>')) {
-                    return valueAsNumber > Number(filterExpressionAsString.substr(1).trim());
+                    return valueAsNumber > Number(filterExpressionAsString.substring(1).trim());
                   } else if (filterExpressionAsString.startsWith('<')) {
-                    return valueAsNumber < Number(filterExpressionAsString.substr(1).trim());
+                    return valueAsNumber < Number(filterExpressionAsString.substring(1).trim());
                   }
                 } else if (column.type === ColumnType.date){
                    const valueAsString = String(dataValue);
@@ -437,13 +480,13 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
                    const filterExpressionAsString = String(filterExpression).trim();             
 
                    if (filterExpressionAsString.startsWith('>=')) {
-                    return valueAsDate >= convertToDate(filterExpressionAsString.substr(2).trim());
+                    return valueAsDate >= convertToDate(filterExpressionAsString.substring(2).trim());
                   } else if (filterExpressionAsString.startsWith('<=')) {
-                    return valueAsDate <= convertToDate(filterExpressionAsString.substr(2).trim());
+                    return valueAsDate <= convertToDate(filterExpressionAsString.substring(2).trim());
                   } else if (filterExpressionAsString.startsWith('>')) {
-                    return valueAsDate > convertToDate(filterExpressionAsString.substr(1).trim());
+                    return valueAsDate > convertToDate(filterExpressionAsString.substring(1).trim());
                   } else if (filterExpressionAsString.startsWith('<')) {
-                    return valueAsDate < convertToDate(filterExpressionAsString.substr(1).trim());
+                    return valueAsDate < convertToDate(filterExpressionAsString.substring(1).trim());
                   }
 
                   
@@ -658,7 +701,7 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate
   }
 }
 
-export type MaterialTableCtorType<TData extends {} = {}> = new () => React.Component<Omit<MaterialTableComponentProps<TData>, 'classes'>>;
+export type MaterialTableCtorType<TData extends {} = {}> = new () => React.Component<DistributiveOmit<MaterialTableComponentProps<TData>, 'classes'>>;
 
 export const MaterialTable = withStyles(styles)(MaterialTableComponent);
 export default MaterialTable;
\ No newline at end of file