feat(xo-web/vm/disks): use SortedTable (#2429)

See #2416
This commit is contained in:
Rajaa.BARHTAOUI 2018-03-27 11:13:05 +02:00 committed by Pierre Donias
parent 0d718bd632
commit 664d648435
4 changed files with 174 additions and 132 deletions

View File

@ -1,8 +1,7 @@
import Component from 'base-component'
import forEach from 'lodash/forEach'
import React from 'react'
import remove from 'lodash/remove'
import { Shortcuts as ReactShortcuts } from 'react-shortcuts'
import { forEach, remove } from 'lodash'
let enabled = true
const instances = []
@ -29,7 +28,20 @@ export default class Shortcuts extends Component {
remove(instances, this)
}
_handler = (command, event) => {
// When an input is focused, shortcuts are disabled by default *except* for
// non-printable keys (Esc, Enter, ...) but we want to disable them as well
// https://github.com/avocode/react-shortcuts/issues/13#issuecomment-255868423
if (event.target.tagName === 'INPUT') {
return
}
this.props.handler(command, event)
}
render () {
return enabled ? <ReactShortcuts {...this.props} /> : null
return enabled ? (
<ReactShortcuts {...this.props} handler={this._handler} />
) : null
}
}

View File

@ -830,8 +830,8 @@ export default class SortedTable extends Component {
{shortcutsTarget !== undefined && (
<Shortcuts
handler={this._getShortcutsHandler()}
isolate
name='SortedTable'
stopPropagation
targetNodeSelector={shortcutsTarget}
/>
)}

View File

@ -912,10 +912,10 @@ export default class Home extends Component {
return (
<Page header={this._renderHeader()}>
<Shortcuts
name='Home'
handler={this._getShortcutsHandler()}
isolate
name='Home'
targetNodeSelector='body'
stopPropagation={false}
/>
<div>
<div className={styles.itemContainer}>

View File

@ -1,6 +1,5 @@
import _, { messages } from 'intl'
import ActionButton from 'action-button'
import ActionRowButton from 'action-row-button'
import Component from 'base-component'
import HTML5Backend from 'react-dnd-html5-backend'
import Icon from 'icon'
@ -10,8 +9,8 @@ import propTypes from 'prop-types-decorator'
import React from 'react'
import SingleLineRow from 'single-line-row'
import StateButton from 'state-button'
import SortedTable from 'sorted-table'
import TabButton from 'tab-button'
import Tooltip from 'tooltip'
import { Container, Row, Col } from 'grid'
import {
createSelector,
@ -33,13 +32,15 @@ import { SizeInput, Toggle } from 'form'
import { XoSelect, Size, Text } from 'editable'
import { confirm } from 'modal'
import { error } from 'notification'
import { forEach, get, isEmpty, map, some } from 'lodash'
import { filter, find, forEach, get, map, mapValues, some } from 'lodash'
import {
attachDiskToVm,
createDisk,
connectVbd,
deleteVbd,
deleteVbds,
deleteVdi,
deleteVdis,
disconnectVbd,
editVdi,
isSrWritable,
@ -50,6 +51,132 @@ import {
subscribeResourceSets,
} from 'xo'
const COLUMNS_VM_PV = [
{
itemRenderer: vdi => (
<Text
value={vdi.name_label}
onChange={value => editVdi(vdi, { name_label: value })}
/>
),
name: _('vdiNameLabel'),
sortCriteria: 'name_label',
default: true,
},
{
itemRenderer: vdi => (
<Text
value={vdi.name_description}
onChange={value => editVdi(vdi, { name_description: value })}
/>
),
name: _('vdiNameDescription'),
sortCriteria: 'name_description',
},
{
itemRenderer: vdi => (
<Size
value={vdi.size || null}
onChange={size => editVdi(vdi, { size })}
/>
),
name: _('vdiSize'),
sortCriteria: 'size',
},
{
itemRenderer: (vdi, userData) => {
const sr = userData.srs[vdi.$SR]
return (
sr !== undefined && (
<XoSelect
labelProp='name_label'
onChange={sr => migrateVdi(vdi, sr)}
predicate={sr => sr.$pool === userData.vm.$pool && isSrWritable(sr)}
useLongClick
value={sr}
xoType='SR'
>
<Link to={`/srs/${sr.id}`}>{sr.name_label}</Link>
</XoSelect>
)
)
},
name: _('vdiSr'),
sortCriteria: (vdi, userData) => {
const sr = userData.srs[vdi.$SR]
return sr !== undefined && sr.name_label
},
},
{
itemRenderer: (vdi, userData) => {
const vbd = userData.vbdsByVdi[vdi.id]
return (
<Toggle
onChange={bootable => setBootableVbd(vbd, bootable)}
value={vbd.bootable}
/>
)
},
name: _('vbdBootableStatus'),
id: 'vbdBootableStatus',
},
{
itemRenderer: (vdi, userData) => {
const vbd = userData.vbdsByVdi[vdi.id]
return (
<StateButton
disabledLabel={_('vbdStatusDisconnected')}
disabledHandler={connectVbd}
disabledTooltip={_('vbdConnect')}
enabledLabel={_('vbdStatusConnected')}
enabledHandler={disconnectVbd}
enabledTooltip={_('vbdDisconnect')}
disabled={!(vbd.attached || isVmRunning(userData.vm))}
handlerParam={vbd}
state={vbd.attached}
/>
)
},
name: _('vbdStatus'),
},
]
const COLUMNS = filter(COLUMNS_VM_PV, col => col.id !== 'vbdBootableStatus')
const ACTIONS = [
{
disabled: (selectedItems, userData) =>
some(map(selectedItems, vdi => userData.vbdsByVdi[vdi.id]), 'attached'),
handler: (selectedItems, userData) =>
deleteVbds(map(selectedItems, vdi => userData.vbdsByVdi[vdi.id])),
individualDisabled: (vdi, userData) => {
const vbd = userData.vbdsByVdi[vdi.id]
return vbd !== undefined && vbd.attached
},
individualHandler: (vdi, userData) => {
const vbd = userData.vbdsByVdi[vdi.id]
return vbd !== undefined && deleteVbd(vbd)
},
icon: 'vdi-forget',
label: _('vdiForget'),
level: 'danger',
},
{
disabled: (selectedItems, userData) =>
some(map(selectedItems, vdi => userData.vbdsByVdi[vdi.id]), 'attached'),
handler: deleteVdis,
individualDisabled: (vdi, userData) => {
const vbd = userData.vbdsByVdi[vdi.id]
return vbd !== undefined && vbd.attached
},
individualHandler: deleteVdi,
individualLabel: _('vdiRemove'),
icon: 'vdi-remove',
label: _('deleteSelectedVdis'),
level: 'danger',
},
]
const parseBootOrder = bootOrder => {
// FIXME missing translation
const bootOptions = {
@ -516,6 +643,21 @@ export default class TabDisks extends Component {
isAdmin || (resourceSet == null && isVmAdmin)
)
_getVbdsByVdi = createSelector(
() => this.props.vdis,
() => this.props.vbds,
() => this.props.vm,
(vdis, vbds, vm) => mapValues(vdis, vdi => find(vbds, { VDI: vdi.id }))
)
individualActions = [
{
handler: this._migrateVdi,
icon: 'vdi-migrate',
label: _('vdiMigrate'),
},
]
render () {
const { srs, vbds, vdis, vm } = this.props
@ -577,129 +719,17 @@ export default class TabDisks extends Component {
</Row>
<Row>
<Col>
{!isEmpty(vbds) ? (
<table className='table'>
<thead className='thead-default'>
<tr>
<th>{_('vdiNameLabel')}</th>
<th>{_('vdiNameDescription')}</th>
<th>{_('vdiSize')}</th>
<th>{_('vdiSr')}</th>
{vm.virtualizationMode === 'pv' && (
<th>{_('vbdBootableStatus')}</th>
)}
<th>{_('vbdStatus')}</th>
<th className='text-xs-right'>{_('vbdAction')}</th>
</tr>
</thead>
<tbody>
{map(vbds, vbd => {
const vdi = vdis[vbd.VDI]
if (vbd.is_cd_drive || !vdi) {
return
}
const sr = srs[vdi.$SR]
return (
<tr key={vbd.id}>
<td>
<Text
value={vdi.name_label}
onChange={value =>
editVdi(vdi, { name_label: value })
}
/>
</td>
<td>
<Text
value={vdi.name_description}
onChange={value =>
editVdi(vdi, { name_description: value })
}
/>
</td>
<td>
<Size
value={vdi.size || null}
onChange={size => editVdi(vdi, { size })}
/>
</td>
<td>
{' '}
{sr && (
<XoSelect
onChange={sr => migrateVdi(vdi, sr)}
xoType='SR'
predicate={sr =>
sr.$pool === vm.$pool && isSrWritable(sr)
}
labelProp='name_label'
value={sr}
useLongClick
>
<Link to={`/srs/${sr.id}`}>{sr.name_label}</Link>
</XoSelect>
)}
</td>
{vm.virtualizationMode === 'pv' && (
<td>
<Toggle
value={vbd.bootable}
onChange={bootable =>
setBootableVbd(vbd, bootable)
}
/>
</td>
)}
<td>
<StateButton
disabledLabel={_('vbdStatusDisconnected')}
disabledHandler={connectVbd}
disabledTooltip={_('vbdConnect')}
enabledLabel={_('vbdStatusConnected')}
enabledHandler={disconnectVbd}
enabledTooltip={_('vbdDisconnect')}
disabled={!(vbd.attached || isVmRunning(vm))}
handlerParam={vbd}
state={vbd.attached}
/>
</td>
<td className='text-xs-right'>
<Tooltip content={_('vdiMigrate')}>
<ActionRowButton
icon='vdi-migrate'
handler={this._migrateVdi}
handlerParam={vdi}
/>
</Tooltip>
{!vbd.attached && (
<span>
<Tooltip content={_('vdiForget')}>
<ActionRowButton
icon='vdi-forget'
handler={deleteVbd}
handlerParam={vbd}
/>
</Tooltip>
<Tooltip content={_('vdiRemove')}>
<ActionRowButton
icon='vdi-remove'
handler={deleteVdi}
handlerParam={vdi}
/>
</Tooltip>
</span>
)}
</td>
</tr>
)
})}
</tbody>
</table>
) : (
<h4 className='text-xs-center'>{_('vbdNoVbd')}</h4>
)}
<SortedTable
actions={ACTIONS}
collection={vdis}
columns={vm.virtualizationMode === 'pv' ? COLUMNS_VM_PV : COLUMNS}
data-srs={srs}
data-vbdsByVdi={this._getVbdsByVdi()}
data-vm={vm}
individualActions={this.individualActions}
shortcutsTarget='body'
stateUrlParam='s'
/>
</Col>
</Row>
<Row>