UNPKG

k2hr3-app

Version:

K2HR3 Web Application is K2hdkc based Resource and Roles and policy Rules

1,456 lines (1,318 loc) 41.5 kB
/* * * K2HR3 Web Application * * Copyright 2017 Yahoo Japan Corporation. * * K2HR3 is K2hdkc based Resource and Roles and policy Rules, gathers * common management information for the cloud. * K2HR3 can dynamically manage information as "who", "what", "operate". * These are stored as roles, resources, policies in K2hdkc, and the * client system can dynamically read and modify these information. * * For the full copyright and license information, please view * the license file that was distributed with this source code. * * AUTHOR: Takeshi Nakatani * CREATE: Tue Aug 15 2017 * REVISION: * */ import React from 'react'; import ReactDOM from 'react-dom'; // eslint-disable-line no-unused-vars import PropTypes from 'prop-types'; import AppBar from '@mui/material/AppBar'; import Toolbar from '@mui/material/Toolbar'; import Drawer from '@mui/material/Drawer'; import Chip from '@mui/material/Chip'; import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; import Divider from '@mui/material/Divider'; import IconButton from '@mui/material/IconButton'; import Tooltip from '@mui/material/Tooltip'; import Typography from '@mui/material/Typography'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import ListItemText from '@mui/material/ListItemText'; import ListItemIcon from '@mui/material/ListItemIcon'; import Collapse from '@mui/material/Collapse'; import Box from '@mui/material/Box'; import ExpandLessIcon from '@mui/icons-material/ExpandLess'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import MenuIcon from '@mui/icons-material/Menu'; import ArrowRightIcon from '@mui/icons-material/ArrowRight'; import LabelIcon from '@mui/icons-material/Label'; import OwnerIcon from '@mui/icons-material/Person'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import EditIcon from '@mui/icons-material/Edit'; import { r3MainTree } from './r3styles'; import R3LocalTenantDialog from './r3localtenantdialog'; import R3PopupMsgDialog from './r3popupmsgdialog'; import R3Message from '../util/r3message'; import { serviceType, errorType } from '../util/r3types'; import { localTenantPrefix } from '../util/r3define'; import { r3DeepCompare, r3IsEmptyEntity, r3IsEmptyStringObject, r3IsEmptyString, r3CompareCaseString, r3IsEmptyEntityObject, r3IsSafeTypedEntity, r3DeepClone } from '../util/r3util'; // For context import { R3CommonContext } from './r3commoncontext'; // // Local variables // const menuValues = { dock: 'DOCK', about: 'ABOUT', license: 'LICENSES_TOP', noLicense: 'NOLICENSE', sign: 'SIGNINOUT', userName: 'USERNAME', localTenant: 'LOCALTENANT' }; const componentKeyIds = { topListId: 'mainTree-top-list', dummyBarMenuId: 'mainTree-dummybar-main-menu', dummyLicenseMenuId: 'mainTree-dummybar-license-menu', tenantMenuId: 'maintree-select-tenant-menu', topList: 'mainTree-top-list', subHeaderList: 'mainTree-subheader-list', dummyBarList: 'mainTree-dummybar-list', noTenantList: 'mainTree-no-tenant-list' }; const tooltipValues = { mainMenu: 'mainMenu', tenantEdit: 'tenantEdit', tenantMenu: 'tenantMenu' }; // // Main TreeView Class // export default class R3MainTree extends React.Component { // Set context as this.context static contextType = R3CommonContext; static propTypes = { r3provider: PropTypes.object.isRequired, title: PropTypes.string.isRequired, open: PropTypes.bool, enDock: PropTypes.bool, isDocking: PropTypes.bool, licensesObj: PropTypes.object, editableLocalTenant: PropTypes.bool, userName: PropTypes.string, tenants: PropTypes.array.isRequired, treeList: PropTypes.array, selectedTenant: PropTypes.object, selectedType: PropTypes.string, selectedService: PropTypes.string, selectedPath: PropTypes.string, onTenantChange: PropTypes.func.isRequired, onLocalTenantCreate: PropTypes.func.isRequired, onLocalTenantChange: PropTypes.func.isRequired, onLocalTenantDelete: PropTypes.func.isRequired, onTypeItemChange: PropTypes.func.isRequired, onListItemChange: PropTypes.func.isRequired, onNameItemInServiceChange: PropTypes.func.isRequired, onTypeInServiceChange: PropTypes.func.isRequired, onListItemInServiceChange: PropTypes.func.isRequired, onPopupClose: PropTypes.func.isRequired, onTreeDocking: PropTypes.func.isRequired, onCheckUpdating: PropTypes.func, onAbout: PropTypes.func.isRequired }; static defaultProps = { open: false, enDock: true, isDocking: true, licensesObj: null, editableLocalTenant: true, userName: null, treeList: null, selectedTenant: null, selectedType: null, selectedService: null, selectedPath: null, onCheckUpdating: null }; state = { r3Message: null, dummyBarMenuAnchorEl: null, dummyLicenseMenuAnchorEl: null, tenantMenuAnchorEl: null, localTenantDialogOpen: false, localTenantCreateMode: false, tooltips: { mainMenuTooltip: false, tenantEditTooltip: false, tenantMenuTooltip: false }, collapseExpands: {} }; constructor(props) { super(props); // Binding(do not define handlers as arrow functions for performance) this.handleTenantEditButton = this.handleTenantEditButton.bind(this); this.handLocalTenantDialogClose = this.handLocalTenantDialogClose.bind(this); this.handleTenantMenuButton = this.handleTenantMenuButton.bind(this); this.handleTenantMenuClose = this.handleTenantMenuClose.bind(this); this.handleTypeItemChange = this.handleTypeItemChange.bind(this); this.handleListItemChange = this.handleListItemChange.bind(this); this.handleNameItemInServiceChange = this.handleNameItemInServiceChange.bind(this); this.handleTypeInServiceChange = this.handleTypeInServiceChange.bind(this); this.handleListItemInServiceChange = this.handleListItemInServiceChange.bind(this); this.handleDummyBarButton = this.handleDummyBarButton.bind(this); this.handleDummyBarMenuClose = this.handleDummyBarMenuClose.bind(this); this.handleTreePopupClose = this.handleTreePopupClose.bind(this); this.handMessageDialogClose = this.handMessageDialogClose.bind(this); // styles this.sxClasses = r3MainTree(props.theme); } handleTenantEditButton(event) // eslint-disable-line no-unused-vars { if(!this.checkContentUpdating()){ return; } this.setState({ localTenantDialogOpen: true, localTenantCreateMode: false, tooltips: { tenantEditTooltip: false } }); } handLocalTenantDialogClose(event, reason, result, newTenantName, tenantId, newTenantDisplay, newTenantDescription, newTenantUsers) { if(!result){ this.setState({ localTenantDialogOpen: false, localTenantCreateMode: false, r3Message: null, tooltips: { tenantEditTooltip: false } }); return; } // [NOTE] // Value checking is completed in R3LocalTenantDialog. // No checks are required here. // // // Call parent handler for update/create tenant // if(this.state.localTenantCreateMode){ // create this.props.onLocalTenantCreate(newTenantName, newTenantDisplay, newTenantDescription, newTenantUsers); }else if(!r3IsSafeTypedEntity(newTenantUsers, 'array') || 0 === newTenantUsers.length){ // delete this.props.onLocalTenantDelete(newTenantName, tenantId); }else{ // update this.props.onLocalTenantChange(newTenantName, tenantId, newTenantDisplay, newTenantDescription, newTenantUsers); } // close tenant dialog this.setState({ localTenantDialogOpen: false, localTenantCreateMode: false, r3Message: null, tooltips: { tenantEditTooltip: false } }); } handleTenantMenuButton(event) { this.setState({ tenantMenuAnchorEl: event.currentTarget, tooltips: { tenantMenuTooltip: false } }); } handleTenantMenuClose(event) // eslint-disable-line no-unused-vars { this.setState({ tenantMenuAnchorEl: null }); } handleTenantChange = (event, value) => { if(this.checkContentUpdating()){ this.props.tenants.map( (item, pos) => { if(value === pos){ // if selected tenant is changed, we need to clear collapseExpands{}. // if( !r3IsEmptyEntityObject(this.props.selectedTenant, 'name') && !r3IsEmptyEntityObject(this.props.selectedTenant, 'display') && !r3IsEmptyEntityObject(this.props.selectedTenant, 'id') && !r3IsEmptyEntityObject(this.props.selectedTenant, 'description')&& !r3IsEmptyEntityObject(item, 'name') && !r3IsEmptyEntityObject(item, 'display') && item.name !== this.props.selectedTenant.name && item.display !== this.props.selectedTenant.display ) { this.setState({ collapseExpands: {} }); } this.props.onTenantChange(item); } }); } // closing menu this.setState({ tenantMenuAnchorEl: null }); }; handleCreateLocalTenant = (event) => // eslint-disable-line no-unused-vars { if(this.checkContentUpdating()){ // open dialog and closing menu this.setState({ localTenantDialogOpen: true, localTenantCreateMode: true, tenantMenuAnchorEl: null }); } // closing menu this.setState({ tenantMenuAnchorEl: null }); }; updateCollapseChange(prevSelected, collapseKey) { let newCollapseExpands = Object.assign({}, this.state.collapseExpands); if(r3IsEmptyEntityObject(newCollapseExpands, collapseKey) || !r3IsSafeTypedEntity(newCollapseExpands[collapseKey], 'boolean')){ // not found collapseKey's state, it means that item does not expand now. if(prevSelected){ // now selected -> expanding newCollapseExpands[collapseKey] = true; // something wrong, but continue... }else{ // now not selected -> expanding newCollapseExpands[collapseKey] = true; } }else{ if(prevSelected){ // now selected -> toggle newCollapseExpands[collapseKey] = !newCollapseExpands[collapseKey]; }else{ // now not selected -> expanding newCollapseExpands[collapseKey] = true; } } this.setState({ collapseExpands: newCollapseExpands }); } handleTypeItemChange(event, type, prevSelected, collapseKey) { if(!this.checkContentUpdating()){ return; } this.updateCollapseChange(prevSelected, collapseKey); this.props.onTypeItemChange(type); } handleListItemChange(event, type, path, prevSelected, collapseKey) { if(!this.checkContentUpdating()){ return; } this.updateCollapseChange(prevSelected, collapseKey); this.props.onListItemChange(type, path); } handleNameItemInServiceChange(event, servicename, prevSelected, collapseKey) { if(!this.checkContentUpdating()){ return; } this.updateCollapseChange(prevSelected, collapseKey); this.props.onNameItemInServiceChange(servicename); } handleTypeInServiceChange(event, servicename, type_in_service, prevSelected, collapseKey) { if(!this.checkContentUpdating()){ return; } this.updateCollapseChange(prevSelected, collapseKey); this.props.onTypeInServiceChange(servicename, type_in_service); } handleListItemInServiceChange(event, servicename, type_in_service, path, prevSelected, collapseKey) { if(!this.checkContentUpdating()){ return; } this.updateCollapseChange(prevSelected, collapseKey); this.props.onListItemInServiceChange(servicename, type_in_service, path); } handleTreePopupClose(event) // eslint-disable-line no-unused-vars { console.info('CALL - Drawer close'); this.props.onPopupClose(); } handleDummyBarButton(event) { this.setState({ dummyBarMenuAnchorEl: event.currentTarget, dummyLicenseMenuAnchorEl: null, tooltips: { mainMenuTooltip: false } }); } handleDummyBarMenuClose(event) // eslint-disable-line no-unused-vars { this.setState({ dummyBarMenuAnchorEl: null, dummyLicenseMenuAnchorEl: null }); } handleDummyBarClick = (event, value) => { // [NOTE] // The sub MenuItem for licenses menu is received in the OnClick event, // not the OnChange event. // Then, the value is undefined in the onClick event, so we change the // behavior of the function with this value. // if(r3IsEmptyEntity(value)){ // Licenses if(!r3IsEmptyString(event.target.innerText)){ this.props.onAbout(event.target.innerText); } }else{ if(menuValues.dock === value){ this.props.onTreeDocking(true); }else if(menuValues.about === value){ this.props.onAbout(null); }else if(menuValues.noLicense === value || menuValues.license === value){ if(!this.state.dummyLicenseMenuAnchorEl){ this.setState({ dummyLicenseMenuAnchorEl: event.currentTarget }); return; } }else if(!isNaN(value)){ let _appmenu= this.context.r3Context.getSafeAppMenu(); let _pos = parseInt(value); if( 0 <= _pos && undefined !== _appmenu && null !== _appmenu && _appmenu instanceof Array && _pos < _appmenu.length && !r3IsEmptyStringObject(_appmenu[_pos], 'url')) { window.open(_appmenu[_pos].url); } } } // to close drawer if(!this.props.isDocking){ this.props.onPopupClose(); } // closing menu this.setState({ dummyBarMenuAnchorEl: null, dummyLicenseMenuAnchorEl: null }); }; handMessageDialogClose(event, reason, result) // eslint-disable-line no-unused-vars { this.setState({ r3Message: null }); } handTooltipChange = (event, type, isOpen) => { if(tooltipValues.mainMenu === type){ this.setState({ tooltips: { mainMenuTooltip: isOpen } }); }else if(tooltipValues.tenantEdit === type){ this.setState({ tooltips: { tenantEditTooltip: isOpen } }); }else if(tooltipValues.tenantMenu === type){ this.setState({ tooltips: { tenantMenuTooltip: isOpen } }); } }; checkContentUpdating() { const { r3provider } = this.props; if(null !== this.props.onCheckUpdating && this.props.onCheckUpdating()){ this.setState({ r3Message: new R3Message(r3provider.getR3TextRes().eNowUpdating, errorType) }); return false; } return true; } getAllTenantNames() { let tenantNames = []; for(let cnt = 0; cnt < this.props.tenants.length; ++cnt){ if(!r3IsEmptyEntityObject(this.props.tenants[cnt], 'name') && !r3IsEmptyString(this.props.tenants[cnt].name)){ tenantNames.push(this.props.tenants[cnt].name); } } tenantNames.sort(); return tenantNames; } getLicensesMenuItems() { const { r3provider } = this.props; let _menuitems = []; if(r3IsEmptyEntity(this.props.licensesObj)){ _menuitems.push( <MenuItem disabled={ true } key={ menuValues.noLicense } > { r3provider.getR3TextRes().tResNoLicenseMenu } </MenuItem> ); }else{ let packages = Object.keys(this.props.licensesObj); for(let cnt = 0; cnt < packages.length; ++cnt){ _menuitems.push( <MenuItem key={ packages[cnt] } onClick={ (event) => this.handleDummyBarClick(event, null) } > { packages[cnt] } </MenuItem> ); } } return _menuitems; } getMenuItems() { const { theme, r3provider } = this.props; let _menuitems = []; if(this.props.enDock){ _menuitems.push( <MenuItem key={ menuValues.dock } onClick={ event => this.handleDummyBarClick(event, menuValues.dock) } > { r3provider.getR3TextRes().tResDockTreeMenu } </MenuItem> ); } let _appmenu = this.context.r3Context.getSafeAppMenu(); if(undefined !== _appmenu && null !== _appmenu && _appmenu instanceof Array && 0 < _appmenu.length){ for(let cnt = 0; cnt < _appmenu.length; ++cnt){ if(r3IsEmptyStringObject(_appmenu[cnt], 'name')){ continue; } _menuitems.push( <MenuItem key={ cnt } onClick={ event => this.handleDummyBarClick(event, cnt) } > { _appmenu[cnt].name } </MenuItem> ); } } // Licenses _menuitems.push( <MenuItem key={ menuValues.license } onClick={ event => this.handleDummyBarClick(event, menuValues.license) } > { r3provider.getR3TextRes().tResLicensesMenu } <ListItemIcon> <ArrowRightIcon sx={ this.sxClasses.menuRightIcon } /> </ListItemIcon> <Menu id={ componentKeyIds.dummyLicenseMenuId } anchorEl={ this.state.dummyLicenseMenuAnchorEl } open={ Boolean(this.state.dummyLicenseMenuAnchorEl) } onClose={ this.handleDummyBarMenuClose } { ...theme.r3MainTree.licenseMenu } > { this.getLicensesMenuItems() } </Menu> </MenuItem> ); // About _menuitems.push( <MenuItem key={ menuValues.about } onClick={ event => this.handleDummyBarClick(event, menuValues.about) } > { r3provider.getR3TextRes().tResAboutMenu } </MenuItem> ); return _menuitems; } getTenantListMenuItems() { const { r3provider } = this.props; if(!(this.props.tenants instanceof Array) || 0 === this.props.tenants.length){ return ( <MenuItem key={ componentKeyIds.noTenantList } disabled={ (r3IsEmptyString(this.props.userName) || !this.props.editableLocalTenant) } > { r3provider.getR3TextRes().tResNoTenantLabel } </MenuItem> ); }else{ return ( this.props.tenants.map( (item, pos) => { return ( <MenuItem key={ pos } onClick={ event => this.handleTenantChange(event, pos) } > { (r3IsEmptyString(item.display) ? r3provider.getR3TextRes().tResUnknownTenantLabel : item.display) } </MenuItem> ); }) ); } } getLocalTenantMenuItem() { const { r3provider } = this.props; if(r3IsEmptyString(this.props.userName) || !this.props.editableLocalTenant){ return; } return ( <MenuItem key={ menuValues.localTenant } onClick={ event => this.handleCreateLocalTenant(event) } > <Typography sx={ this.sxClasses.localTenantMenu } > { r3provider.getR3TextRes().tResAddLocalTenantMenu } </Typography> </MenuItem> ); } getLocalTenantMenuDivider() { if(r3IsEmptyString(this.props.userName) || !this.props.editableLocalTenant){ return; } return ( <Divider /> ); } getDummyBarSubHeader() { const { theme, r3provider } = this.props; let themeToolbar = this.props.enDock ? theme.r3MainTree.dummyBarToolbar : theme.r3MainTree.smallDummyBarToolbar; return ( <AppBar { ...theme.r3MainTree.dummyBarAppbar } sx={ this.sxClasses.dummyBarAppbar } > <Toolbar { ...themeToolbar } sx={ this.sxClasses.dummyBarToolbar } > <Tooltip title={ r3provider.getR3TextRes().tResMainMenuTT } open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.mainMenuTooltip, 'boolean')) ? false : this.state.tooltips.mainMenuTooltip) } > <IconButton onClick={ this.handleDummyBarButton } onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.mainMenu, true) } onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.mainMenu, false) } aria-owns={ this.state.dummyBarMenuAnchorEl ? componentKeyIds.dummyBarMenuId : undefined } { ...theme.r3MainTree.dummyBarMainMenuButton } size="large" > <MenuIcon /> </IconButton> </Tooltip> <Menu id={ componentKeyIds.dummyBarMenuId } anchorEl={ this.state.dummyBarMenuAnchorEl } open={ Boolean(this.state.dummyBarMenuAnchorEl) } onClose={ this.handleDummyBarMenuClose } { ...theme.r3MainTree.dummyBarMainMenu } > { this.getMenuItems() } </Menu> <Typography { ...theme.r3MainTree.title } ex={ this.sxClasses.title } > { this.props.title } </Typography> </Toolbar> </AppBar> ); } getTenantSubHeaders() { const { theme, r3provider } = this.props; let menuItem = this.getTenantListMenuItems(); let menuLocalTenantItem = this.getLocalTenantMenuItem(); let menuLocalTenantDivider = this.getLocalTenantMenuDivider(); let titleName = ((this.props.tenants instanceof Array && 0 < this.props.tenants.length) ? r3provider.getR3TextRes().tResUnselectedTenantLabel : r3provider.getR3TextRes().tResNoTenantLabel); let themeToolbar = (this.props.enDock ? theme.r3MainTree.subheaderToolbar : theme.r3MainTree.smallSubheaderToolbar); this.props.tenants.map( (tenant, pos) => { // eslint-disable-line no-unused-vars if(r3DeepCompare(tenant, this.props.selectedTenant)){ titleName = tenant.display; } }); // // Local tenant edit button // let tenantEditBtn; if( !r3IsEmptyString(this.props.userName) && this.props.editableLocalTenant && !r3IsEmptyEntityObject(this.props.selectedTenant, 'name') && 0 === this.props.selectedTenant.name.indexOf(localTenantPrefix) && r3IsSafeTypedEntity(this.props.selectedTenant.users, 'array') ) { tenantEditBtn = ( <Tooltip title={ r3provider.getR3TextRes().tResTenantEditTT } open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.tenantEditTooltip, 'boolean')) ? false : this.state.tooltips.tenantEditTooltip) } > <IconButton onClick={ this.handleTenantEditButton } onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.tenantEdit, true) } onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.tenantEdit, false) } { ...theme.r3MainTree.tenantEditButton } size="large" > <EditIcon /> </IconButton> </Tooltip> ); } let textLabel = ( <Typography { ...theme.r3MainTree.chipText } sx={ this.sxClasses.chipText } > { r3provider.getR3TextRes().tResTenantLabel } </Typography> ); return ( <AppBar { ...theme.r3MainTree.subheaderAppbar } sx={ this.sxClasses.subheaderAppbar } > <Toolbar { ...themeToolbar } sx={ this.sxClasses.subheaderToolbar } > <Chip label={ textLabel } { ...theme.r3MainTree.chip } sx={ this.sxClasses.chip } /> <Typography { ...theme.r3MainTree.tenantListText } sx={ this.sxClasses.tenantListText } > { titleName } </Typography> { tenantEditBtn } <Tooltip title={ r3provider.getR3TextRes().tResSelectTenantTT } open={ ((r3IsEmptyEntityObject(this.state, 'tooltips') || !r3IsSafeTypedEntity(this.state.tooltips.tenantMenuTooltip, 'boolean')) ? false : this.state.tooltips.tenantMenuTooltip) } > <IconButton disabled={ r3IsEmptyString(this.props.userName) } aria-owns={ this.state.tenantMenuAnchorEl ? componentKeyIds.tenantMenuId : undefined } onClick={ this.handleTenantMenuButton } onMouseEnter={ event => this.handTooltipChange(event, tooltipValues.tenantMenu, true) } onMouseLeave={ event => this.handTooltipChange(event, tooltipValues.tenantMenu, false) } { ...theme.r3MainTree.tenantListButton } size="large" > <MoreVertIcon /> </IconButton> </Tooltip> <Menu id={ componentKeyIds.tenantMenuId } anchorEl={ this.state.tenantMenuAnchorEl } open={ Boolean(this.state.tenantMenuAnchorEl) } onClose={ this.handleTenantMenuClose } { ...theme.r3MainTree.tenantListMenu } > { menuItem } { menuLocalTenantDivider } { menuLocalTenantItem } </Menu> </Toolbar> </AppBar> ); } getSubHeaders() { let dummyBarSubHeader; if(!this.props.isDocking){ dummyBarSubHeader = this.getDummyBarSubHeader(); } return ( <Box> { dummyBarSubHeader } { this.getTenantSubHeaders() } </Box> ); } // // For children under TOP > ROLE/POLICY/RESOURCE // getChildrenListItems(treeList, type) { const { theme } = this.props; if(undefined === treeList || !(treeList instanceof Array)){ return; } let _type = type; return ( treeList.map( (item, pos) => { // eslint-disable-line no-unused-vars let listItemKey = _type + '_' + item.path; let isSelected = (undefined != item.selected ? true : false); let isCollapseExpand = false; if(!r3IsEmptyEntityObject(this.state.collapseExpands, listItemKey) && r3IsSafeTypedEntity(this.state.collapseExpands[listItemKey], 'boolean')){ isCollapseExpand = this.state.collapseExpands[listItemKey]; } let expandIcon; let collapseItem; if(undefined !== item.children && (item.children instanceof Array) && 0 < item.children.length){ let childrenItem = this.getChildrenListItems(item.children, _type); if(childrenItem){ if(isCollapseExpand){ expandIcon = ( <ListItemIcon ex={ this.sxClasses.expandListItemIcon } > <ExpandLessIcon /> </ListItemIcon> ); }else{ expandIcon = ( <ListItemIcon sx={ this.sxClasses.expandListItemIcon } > <ExpandMoreIcon /> </ListItemIcon> ); } collapseItem = ( <Collapse in={ isCollapseExpand } { ...theme.r3MainTree.collapse } sx={ this.sxClasses.collapse } > <List { ...theme.r3MainTree.list } > { childrenItem } </List> </Collapse> ); } } let accordingItemText; if(undefined !== item.selected && item.selected){ accordingItemText = theme.r3MainTree.childSelectedItemText; }else{ accordingItemText = theme.r3MainTree.childItemText; } // [NOTE] // Returning React.Fragment as a top component causes a key props error, so we use div to avoid it. // return ( <Box key={ listItemKey } > <ListItem onClick={ (event) => this.handleListItemChange(event, _type, item.path, isSelected, listItemKey) } { ...theme.r3MainTree.listItem } > <ListItemText primary={ item.name } { ...accordingItemText } sx={ this.sxClasses.childListItemText } /> { expandIcon } </ListItem> { collapseItem } </Box> ); }) ); } // // For Service Name(TOP) under TOP > SERVICE // getServicesListItems(treeList) { const { theme } = this.props; if(undefined === treeList || !(treeList instanceof Array)){ return; } return ( treeList.map( (item, pos) => { // eslint-disable-line no-unused-vars let listItemKey = 'service_' + item.path; let isSelected = (undefined != item.selected ? true : false); let isCollapseExpand = false; if(!r3IsEmptyEntityObject(this.state.collapseExpands, listItemKey) && r3IsSafeTypedEntity(this.state.collapseExpands[listItemKey], 'boolean')){ isCollapseExpand = this.state.collapseExpands[listItemKey]; } let ownerIcon; if(r3IsSafeTypedEntity(item.owner, 'boolean') && true === item.owner){ ownerIcon = ( <ListItemIcon> <OwnerIcon { ...theme.r3MainTree.editIcon } /> </ListItemIcon> ); } let expandIcon; let collapseItem; if(undefined !== item.children && (item.children instanceof Array) && 0 < item.children.length){ let childrenItem = this.getServiceMainListItems(item.children, item.path); if(childrenItem){ if(isCollapseExpand){ expandIcon = ( <ListItemIcon sx={ this.sxClasses.expandListItemIcon } > <ExpandLessIcon /> </ListItemIcon> ); }else{ expandIcon = ( <ListItemIcon sx={ this.sxClasses.expandListItemIcon } > <ExpandMoreIcon /> </ListItemIcon> ); } collapseItem = ( <Collapse in={ isCollapseExpand } { ...theme.r3MainTree.collapse } sx={ this.sxClasses.serviceLabelCollapse } > <List { ...theme.r3MainTree.list } > { childrenItem } </List> </Collapse> ); } } let accordingItemText; if(undefined !== item.selected && item.selected){ accordingItemText = theme.r3MainTree.childSelectedItemText; }else{ accordingItemText = theme.r3MainTree.childItemText; } // [NOTE] // Returning React.Fragment as a top component causes a key props error, so we use div to avoid it. // return ( <Box key={ listItemKey } > <ListItem onClick={ (event) => this.handleNameItemInServiceChange(event, item.path, isSelected, listItemKey) } { ...theme.r3MainTree.listItem } > <ListItemText primary={ item.name } { ...accordingItemText } sx={ this.sxClasses.childListItemText } /> { ownerIcon } { expandIcon } </ListItem> { collapseItem } </Box> ); }) ); } // // For Type(ROLE/POLICY/RESOURCE) under TOP > SERVICE > Service Name(TOP) // // service_name : "service name" // getServiceMainListItems(treeList, service_name) { const { theme } = this.props; if(undefined === treeList || !(treeList instanceof Array)){ return; } let _service_name = service_name; return ( treeList.map( (item, pos) => { // eslint-disable-line no-unused-vars let listItemKey = 'service_' + _service_name + '_' + item.path; let isSelected = (undefined != item.selected ? true : false); let isCollapseExpand = false; if(!r3IsEmptyEntityObject(this.state.collapseExpands, listItemKey) && r3IsSafeTypedEntity(this.state.collapseExpands[listItemKey], 'boolean')){ isCollapseExpand = this.state.collapseExpands[listItemKey]; } let expandIcon; let collapseItem; if(undefined !== item.children && (item.children instanceof Array) && 0 < item.children.length){ let childrenItem = this.getServiceChildrenListItems(item.children, _service_name, item.path); if(childrenItem){ if(isCollapseExpand){ expandIcon = ( <ListItemIcon sx={ this.sxClasses.expandListItemIcon } > <ExpandLessIcon /> </ListItemIcon> ); }else{ expandIcon = ( <ListItemIcon sx={ this.sxClasses.expandListItemIcon } > <ExpandMoreIcon /> </ListItemIcon> ); } collapseItem = ( <Collapse in={ isCollapseExpand } { ...theme.r3MainTree.collapse } sx={ this.sxClasses.collapse } > <List { ...theme.r3MainTree.list } > { childrenItem } </List> </Collapse> ); } } let accordingItemText; if(undefined !== item.selected && item.selected){ accordingItemText = theme.r3MainTree.topSelectedItemText; }else{ accordingItemText = theme.r3MainTree.topItemText; } // [NOTE] // Returning React.Fragment as a top component causes a key props error, so we use div to avoid it. // return ( <Box key={ listItemKey } > <ListItem onClick={ (event) => this.handleTypeInServiceChange(event, _service_name, item.path, isSelected, listItemKey) } { ...theme.r3MainTree.listItem } > <ListItemIcon> <LabelIcon /> </ListItemIcon> <ListItemText primary={ item.name } { ...accordingItemText } sx={ this.sxClasses.inServiceLabelListItemText } /> { expandIcon } </ListItem> { collapseItem } </Box> ); }) ); } // // For Item under TOP > SERVICE > Service Name(TOP) > Type(ROLE/POLICY/RESOURCE) // // service_name : "service name" // type_in_service : "type" under service // getServiceChildrenListItems(treeList, service_name, type_in_service) { const { theme } = this.props; if(undefined === treeList || !(treeList instanceof Array)){ return; } let _service_name = service_name; let _type_in_service = type_in_service; return ( treeList.map( (item, pos) => { // eslint-disable-line no-unused-vars let listItemKey = 'service_' + _service_name + '_' + _type_in_service + '_' + item.path; let isSelected = (undefined != item.selected ? true : false); let isCollapseExpand = false; if(!r3IsEmptyEntityObject(this.state.collapseExpands, listItemKey) && r3IsSafeTypedEntity(this.state.collapseExpands[listItemKey], 'boolean')){ isCollapseExpand = this.state.collapseExpands[listItemKey]; } let expandIcon; let collapseItem; if(undefined !== item.children && (item.children instanceof Array) && 0 < item.children.length){ let childrenItem = this.getServiceChildrenListItems(item.children, _service_name, _type_in_service); if(childrenItem){ if(isCollapseExpand){ expandIcon = ( <ListItemIcon sx={ this.sxClasses.expandListItemIcon } > <ExpandLessIcon /> </ListItemIcon> ); }else{ expandIcon = ( <ListItemIcon sx={ this.sxClasses.expandListItemIcon } > <ExpandMoreIcon /> </ListItemIcon> ); } collapseItem = ( <Collapse in={ isCollapseExpand } { ...theme.r3MainTree.collapse } sx={ this.sxClasses.collapse } > <List { ...theme.r3MainTree.list } > { childrenItem } </List> </Collapse> ); } } let accordingItemText; if(undefined !== item.selected && item.selected){ accordingItemText = theme.r3MainTree.childSelectedItemText; }else{ accordingItemText = theme.r3MainTree.childItemText; } // [NOTE] // Returning React.Fragment as a top component causes a key props error, so we use div to avoid it. // return ( <Box key={ listItemKey } > <ListItem onClick={ (event) => this.handleListItemInServiceChange(event, _service_name, _type_in_service, item.path, isSelected, listItemKey) } { ...theme.r3MainTree.listItem } > <ListItemText primary={ item.name } { ...accordingItemText } sx={ this.sxClasses.inServiceChildListItemText } /> { expandIcon } </ListItem> { collapseItem } </Box> ); }) ); } // // For TOP // getMainListItems(treeList) { const { theme } = this.props; console.info('CALL : getMainListItems'); if(undefined === treeList || !(treeList instanceof Array)){ return; } return ( treeList.map( (item, pos) => // eslint-disable-line no-unused-vars { let listItemKey = 'top_' + item.path; let isSelected = (undefined != item.selected ? true : false); let isCollapseExpand = false; if(!r3IsEmptyEntityObject(this.state.collapseExpands, listItemKey) && r3IsSafeTypedEntity(this.state.collapseExpands[listItemKey], 'boolean')){ isCollapseExpand = this.state.collapseExpands[listItemKey]; } let expandIcon; let collapseItem; if(r3IsSafeTypedEntity(item.children, 'array') && 0 < item.children.length){ let childrenItem; if(r3CompareCaseString(serviceType, item.path)){ childrenItem = this.getServicesListItems(item.children); }else{ childrenItem = this.getChildrenListItems(item.children, item.path); } if(childrenItem){ if(isCollapseExpand){ expandIcon = ( <ListItemIcon sx={ this.sxClasses.expandListItemIcon } > <ExpandLessIcon /> </ListItemIcon> ); }else{ expandIcon = ( <ListItemIcon sx={ this.sxClasses.expandListItemIcon } > <ExpandMoreIcon /> </ListItemIcon> ); } collapseItem = ( <Collapse in={ isCollapseExpand } { ...theme.r3MainTree.collapse } > <List { ...theme.r3MainTree.list } > { childrenItem } </List> </Collapse> ); } } let accordingItemText; if(undefined !== item.selected && item.selected){ accordingItemText = theme.r3MainTree.topSelectedItemText; }else{ accordingItemText = theme.r3MainTree.topItemText; } // [NOTE] // Returning React.Fragment as a top component causes a key props error, so we use div to avoid it. // return ( <Box key={ listItemKey } > <ListItem onClick={ (event) => this.handleTypeItemChange(event, item.path, isSelected, listItemKey) } { ...theme.r3MainTree.listItem } > <ListItemIcon> <LabelIcon /> </ListItemIcon> <ListItemText primary={ item.name } { ...accordingItemText } sx={ this.sxClasses.listItemText } /> { expandIcon } </ListItem> { collapseItem } </Box> ); }) ); } getPopupMessageDialog() { return ( <R3PopupMsgDialog theme={ this.props.theme } r3provider={ this.props.r3provider } r3Message={ this.state.r3Message } onClose={ this.handMessageDialogClose } /> ); } getMainTreeLists() { const { theme } = this.props; return ( <React.Fragment> <Box sx={ (this.props.isDocking ? this.sxClasses.dockedList : {}) } > { this.getSubHeaders() } <List id={ componentKeyIds.topListId } key={ componentKeyIds.topList } { ...theme.r3MainTree.list } > { this.getMainListItems(this.props.treeList) } </List> { this.getPopupMessageDialog() } </Box> <R3LocalTenantDialog theme={ theme } r3provider={ this.props.r3provider } open={ this.state.localTenantDialogOpen } userName={ this.props.userName } createMode={ this.state.localTenantCreateMode } allTenantNames={ this.getAllTenantNames() } tenantName={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'name')) ? '' : this.props.selectedTenant.name } tenantId={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'id')) ? '' : this.props.selectedTenant.id } tenantDisplay={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'display')) ? '' : this.props.selectedTenant.display } tenantDescription={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'description')) ? '' : this.props.selectedTenant.description } tenantUsers={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'users') || !r3IsSafeTypedEntity(this.props.selectedTenant.users, 'array')) ? [ this.props.userName ] : r3DeepClone(this.props.selectedTenant.users).sort() } onClose={ this.handLocalTenantDialogClose } /> </React.Fragment> ); } render() { const { theme } = this.props; if(this.props.isDocking){ return this.getMainTreeLists(); }else{ // [NOTE] // Since witdh=500 maybe used in the calculation as the initial value, // it must be specified.(width=256 in default Theme does not work for me) // And containerStyle must be specified. // return ( <React.Fragment> <Drawer open={ this.props.open } onClose={ (event) => this.handleTreePopupClose(event) } sx={ this.sxClasses.drawer } > <Box sx={ this.sxClasses.adjustment } > { this.getMainTreeLists() } </Box> </Drawer> <R3LocalTenantDialog theme={ theme } r3provider={ this.props.r3provider } open={ this.state.localTenantDialogOpen } userName={ this.props.userName } createMode={ this.state.localTenantCreateMode } allTenantNames={ this.getAllTenantNames() } tenantName={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'name')) ? '' : this.props.selectedTenant.name } tenantId={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'id')) ? '' : this.props.selectedTenant.id } tenantDisplay={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'display')) ? '' : this.props.selectedTenant.display } tenantDescription={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'description')) ? '' : this.props.selectedTenant.description } tenantUsers={ (this.state.localTenantCreateMode || r3IsEmptyEntity(this.props.selectedTenant) || r3IsEmptyEntityObject(this.props.selectedTenant, 'users') || !r3IsSafeTypedEntity(this.props.selectedTenant.users, 'array')) ? [ this.props.userName ] : r3DeepClone(this.props.selectedTenant.users).sort() } onClose={ this.handLocalTenantDialogClose } /> </React.Fragment> ); } } } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noexpandtab sw=4 ts=4 fdm=marker * vim<600: noexpandtab sw=4 ts=4 */