chore(package): update react-virtualized (#2556)
`form/select` has been redeveloped for the occasion with the following features: - not clearable when required and not multiple - when no options available, marked as loading - when required and only one option, it is automatically selected
This commit is contained in:
parent
23e992de85
commit
dc12896b19
@ -132,7 +132,7 @@
|
||||
"react-shortcuts": "^2.0.0",
|
||||
"react-sparklines": "1.6.0",
|
||||
"react-test-renderer": "^15.6.2",
|
||||
"react-virtualized": "^8.0.8",
|
||||
"react-virtualized": "^9.15.0",
|
||||
"readable-stream": "^2.3.3",
|
||||
"redux": "^3.7.2",
|
||||
"redux-thunk": "^2.0.1",
|
||||
|
@ -10,7 +10,7 @@ import getEventValue from './get-event-value'
|
||||
const VERBOSE = false
|
||||
|
||||
const get = (object, path, depth) => {
|
||||
if (depth >= path.length) {
|
||||
if (object == null || depth >= path.length) {
|
||||
return object
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@ import propTypes from '../prop-types-decorator'
|
||||
import { formatSizeRaw, parseSize } from '../utils'
|
||||
|
||||
export Select from './select'
|
||||
export SelectPlainObject from './select-plain-object'
|
||||
|
||||
// ===================================================================
|
||||
|
||||
|
@ -1,118 +0,0 @@
|
||||
import uncontrollableInput from 'uncontrollable-input'
|
||||
import Component from 'base-component'
|
||||
import find from 'lodash/find'
|
||||
import map from 'lodash/map'
|
||||
import React from 'react'
|
||||
|
||||
import propTypes from '../prop-types-decorator'
|
||||
|
||||
import Select from './select'
|
||||
|
||||
@propTypes({
|
||||
autoFocus: propTypes.bool,
|
||||
disabled: propTypes.bool,
|
||||
optionRenderer: propTypes.func,
|
||||
multi: propTypes.bool,
|
||||
onChange: propTypes.func,
|
||||
options: propTypes.array,
|
||||
placeholder: propTypes.node,
|
||||
predicate: propTypes.func,
|
||||
required: propTypes.bool,
|
||||
value: propTypes.any,
|
||||
})
|
||||
@uncontrollableInput()
|
||||
export default class SelectPlainObject extends Component {
|
||||
componentDidMount () {
|
||||
const { options, value } = this.props
|
||||
|
||||
this.setState({
|
||||
options: this._computeOptions(options),
|
||||
value: this._computeValue(value, this.props),
|
||||
})
|
||||
}
|
||||
|
||||
componentWillReceiveProps (newProps) {
|
||||
if (newProps !== this.props) {
|
||||
this.setState({
|
||||
options: this._computeOptions(newProps.options),
|
||||
value: this._computeValue(newProps.value, newProps),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_computeValue (value, props = this.props) {
|
||||
let { optionKey } = props
|
||||
optionKey || (optionKey = 'id')
|
||||
const reduceValue = value =>
|
||||
value != null ? value[optionKey] || value : ''
|
||||
if (props.multi) {
|
||||
if (!Array.isArray(value)) {
|
||||
value = [value]
|
||||
}
|
||||
return map(value, reduceValue)
|
||||
}
|
||||
|
||||
return reduceValue(value)
|
||||
}
|
||||
|
||||
_computeOptions (options) {
|
||||
const { optionKey = 'id' } = this.props
|
||||
const { optionRenderer = o => o.label || o[optionKey] || o } = this.props
|
||||
return map(options, option => ({
|
||||
value: option[optionKey] || option,
|
||||
label: optionRenderer(option),
|
||||
}))
|
||||
}
|
||||
|
||||
_getObject (value) {
|
||||
if (value == null) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const { optionKey = 'id', options } = this.props
|
||||
|
||||
const pickValue = value => {
|
||||
value = value.value || value
|
||||
return find(
|
||||
options,
|
||||
option => option[optionKey] === value || option === value
|
||||
)
|
||||
}
|
||||
|
||||
if (this.props.multi) {
|
||||
return map(value, pickValue)
|
||||
}
|
||||
|
||||
return pickValue(value)
|
||||
}
|
||||
|
||||
_handleChange = value => {
|
||||
const { onChange } = this.props
|
||||
|
||||
if (onChange) {
|
||||
onChange(this._getObject(value))
|
||||
}
|
||||
}
|
||||
|
||||
_renderOption = option => option.label
|
||||
|
||||
render () {
|
||||
const { props, state } = this
|
||||
|
||||
return (
|
||||
<Select
|
||||
autoFocus={props.autoFocus}
|
||||
disabled={props.disabled}
|
||||
multi={props.multi}
|
||||
onChange={this._handleChange}
|
||||
openOnFocus
|
||||
optionRenderer={this._renderOption}
|
||||
options={state.options}
|
||||
placeholder={props.placeholder}
|
||||
required={props.required}
|
||||
value={state.value}
|
||||
valueRenderer={this._renderOption}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
@ -1,138 +1,160 @@
|
||||
import map from 'lodash/map'
|
||||
import React, { Component } from 'react'
|
||||
import classNames from 'classnames'
|
||||
import isEmpty from 'lodash/isEmpty'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import ReactSelect from 'react-select'
|
||||
import sum from 'lodash/sum'
|
||||
import { AutoSizer, CellMeasurer, List } from 'react-virtualized'
|
||||
|
||||
import propTypes from '../prop-types-decorator'
|
||||
|
||||
const SELECT_MENU_STYLE = {
|
||||
overflow: 'hidden',
|
||||
}
|
||||
import uncontrollableInput from 'uncontrollable-input'
|
||||
import {
|
||||
AutoSizer,
|
||||
CellMeasurer,
|
||||
CellMeasurerCache,
|
||||
List,
|
||||
} from 'react-virtualized'
|
||||
|
||||
const SELECT_STYLE = {
|
||||
minWidth: '10em',
|
||||
}
|
||||
|
||||
const LIST_STYLE = {
|
||||
whiteSpace: 'normal',
|
||||
const MENU_STYLE = {
|
||||
overflow: 'hidden',
|
||||
}
|
||||
|
||||
const MAX_OPTIONS = 5
|
||||
|
||||
// See: https://github.com/bvaughn/react-virtualized-select/blob/master/source/VirtualizedSelect/VirtualizedSelect.js
|
||||
@propTypes({
|
||||
maxHeight: propTypes.number,
|
||||
})
|
||||
export default class Select extends Component {
|
||||
@uncontrollableInput()
|
||||
export default class Select extends React.PureComponent {
|
||||
static defaultProps = {
|
||||
maxHeight: 200,
|
||||
optionRenderer: (option, labelKey) => option[labelKey],
|
||||
|
||||
multi: ReactSelect.defaultProps.multi,
|
||||
options: [],
|
||||
required: ReactSelect.defaultProps.required,
|
||||
valueKey: ReactSelect.defaultProps.valueKey,
|
||||
}
|
||||
|
||||
_renderMenu = ({ focusedOption, options, ...otherOptions }) => {
|
||||
static propTypes = {
|
||||
autoSelectSingleOption: PropTypes.bool, // default to props.required
|
||||
maxHeight: PropTypes.number,
|
||||
options: PropTypes.array.isRequired, // cannot be an object
|
||||
}
|
||||
|
||||
_cellMeasurerCache = new CellMeasurerCache({
|
||||
fixedWidth: true,
|
||||
})
|
||||
|
||||
// https://github.com/JedWatson/react-select/blob/dd32c27d7ea338a93159da5e40bc06697d0d86f9/src/utils/defaultMenuRenderer.js#L4
|
||||
_renderMenu (opts) {
|
||||
const { focusOption, options, selectValue } = opts
|
||||
|
||||
const focusFromEvent = event =>
|
||||
focusOption(options[event.currentTarget.dataset.index])
|
||||
const selectFromEvent = event =>
|
||||
selectValue(options[event.currentTarget.dataset.index])
|
||||
const renderRow = opts2 =>
|
||||
this._renderRow(opts, opts2, focusFromEvent, selectFromEvent)
|
||||
|
||||
let focusedOptionIndex = options.indexOf(opts.focusedOption)
|
||||
if (focusedOptionIndex === -1) {
|
||||
focusedOptionIndex = undefined
|
||||
}
|
||||
|
||||
const { length } = options
|
||||
const { maxHeight } = this.props
|
||||
const { rowHeight } = this._cellMeasurerCache
|
||||
|
||||
const focusedOptionIndex = options.indexOf(focusedOption)
|
||||
let height = options.length > MAX_OPTIONS && maxHeight
|
||||
|
||||
const wrappedRowRenderer = ({ index, key, style }) =>
|
||||
this._optionRenderer({
|
||||
...otherOptions,
|
||||
focusedOption,
|
||||
focusedOptionIndex,
|
||||
key,
|
||||
option: options[index],
|
||||
options,
|
||||
style,
|
||||
})
|
||||
let height = 0
|
||||
for (let i = 0; i < length; ++i) {
|
||||
height += rowHeight({ index: i })
|
||||
if (height > maxHeight) {
|
||||
height = maxHeight
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) =>
|
||||
width ? (
|
||||
<CellMeasurer
|
||||
cellRenderer={({ rowIndex }) =>
|
||||
wrappedRowRenderer({ index: rowIndex })
|
||||
}
|
||||
columnCount={1}
|
||||
rowCount={options.length}
|
||||
// FIXME: 16 px: ugly workaround to take into account the scrollbar
|
||||
// during the offscreen render to measure the row height
|
||||
// See https://github.com/bvaughn/react-virtualized/issues/401
|
||||
width={width - 16}
|
||||
>
|
||||
{({ getRowHeight }) => {
|
||||
if (options.length <= MAX_OPTIONS) {
|
||||
height = sum(
|
||||
map(options, (_, index) => getRowHeight({ index }))
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<List
|
||||
height={height}
|
||||
rowCount={options.length}
|
||||
rowHeight={getRowHeight}
|
||||
rowRenderer={wrappedRowRenderer}
|
||||
scrollToIndex={focusedOptionIndex}
|
||||
style={LIST_STYLE}
|
||||
width={width}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
</CellMeasurer>
|
||||
) : null
|
||||
}
|
||||
{({ width }) => (
|
||||
<List
|
||||
deferredMeasurementCache={this._cellMeasurerCache}
|
||||
height={height}
|
||||
rowCount={length}
|
||||
rowHeight={rowHeight}
|
||||
rowRenderer={renderRow}
|
||||
scrollToIndex={focusedOptionIndex}
|
||||
width={width}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
)
|
||||
}
|
||||
_renderMenu = this._renderMenu.bind(this)
|
||||
|
||||
_optionRenderer = ({
|
||||
focusedOption,
|
||||
focusOption,
|
||||
key,
|
||||
labelKey,
|
||||
option,
|
||||
style,
|
||||
selectValue,
|
||||
}) => {
|
||||
let className = 'Select-option'
|
||||
|
||||
if (option === focusedOption) {
|
||||
className += ' is-focused'
|
||||
}
|
||||
|
||||
_renderRow (
|
||||
{
|
||||
focusedOption,
|
||||
focusOption,
|
||||
inputValue,
|
||||
optionClassName,
|
||||
optionRenderer,
|
||||
options,
|
||||
selectValue,
|
||||
},
|
||||
{ index, key, parent, style },
|
||||
focusFromEvent,
|
||||
selectFromEvent
|
||||
) {
|
||||
const option = options[index]
|
||||
const { disabled } = option
|
||||
|
||||
if (disabled) {
|
||||
className += ' is-disabled'
|
||||
}
|
||||
|
||||
const { props } = this
|
||||
|
||||
return (
|
||||
<div
|
||||
className={className}
|
||||
onClick={disabled ? undefined : () => selectValue(option)}
|
||||
onMouseOver={disabled ? undefined : () => focusOption(option)}
|
||||
style={style}
|
||||
<CellMeasurer
|
||||
cache={this._cellMeasurerCache}
|
||||
columnIndex={0}
|
||||
key={key}
|
||||
parent={parent}
|
||||
rowIndex={index}
|
||||
>
|
||||
{props.optionRenderer(option, labelKey)}
|
||||
</div>
|
||||
<div
|
||||
className={classNames('Select-option', optionClassName, {
|
||||
'is-disabled': disabled,
|
||||
'is-focused': option === focusedOption,
|
||||
})}
|
||||
data-index={index}
|
||||
onClick={disabled ? undefined : selectFromEvent}
|
||||
onMouseEnter={disabled ? undefined : focusFromEvent}
|
||||
style={style}
|
||||
title={option.title}
|
||||
>
|
||||
{optionRenderer(option, index, inputValue)}
|
||||
</div>
|
||||
</CellMeasurer>
|
||||
)
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.componentDidUpdate()
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
const { props } = this
|
||||
const { autoSelectSingleOption = props.required, options } = props
|
||||
if (autoSelectSingleOption && options != null && options.length === 1) {
|
||||
const value = options[0][props.valueKey]
|
||||
props.onChange(props.multi ? [value] : value)
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { props } = this
|
||||
const { multi } = props
|
||||
return (
|
||||
<ReactSelect
|
||||
closeOnSelect={!this.props.multi}
|
||||
{...this.props}
|
||||
backspaceToRemoveMessage=''
|
||||
menuRenderer={this._renderMenu}
|
||||
menuStyle={SELECT_MENU_STYLE}
|
||||
clearable={multi || !props.required}
|
||||
closeOnSelect={!multi}
|
||||
isLoading={!props.disabled && isEmpty(props.options)}
|
||||
style={SELECT_STYLE}
|
||||
valueRenderer={props.optionRenderer}
|
||||
{...props}
|
||||
menuRenderer={this._renderMenu}
|
||||
menuStyle={MENU_STYLE}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import classNames from 'classnames'
|
||||
import PropTypes from 'prop-types'
|
||||
import { parse as parseRemote } from 'xo-remote-parser'
|
||||
import {
|
||||
assign,
|
||||
@ -23,14 +23,12 @@ import {
|
||||
|
||||
import _ from './intl'
|
||||
import Button from './button'
|
||||
import Component from './base-component'
|
||||
import Icon from './icon'
|
||||
import propTypes from './prop-types-decorator'
|
||||
import renderXoItem from './render-xo-item'
|
||||
import Select from './form/select'
|
||||
import store from './store'
|
||||
import Tooltip from './tooltip'
|
||||
import uncontrollableInput from 'uncontrollable-input'
|
||||
import { Select } from './form'
|
||||
import {
|
||||
createCollectionWrapper,
|
||||
createFilter,
|
||||
@ -112,23 +110,18 @@ const options = props => ({
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
@propTypes({
|
||||
autoFocus: propTypes.bool,
|
||||
clearable: propTypes.bool,
|
||||
disabled: propTypes.bool,
|
||||
hasSelectAll: propTypes.bool,
|
||||
multi: propTypes.bool,
|
||||
onChange: propTypes.func,
|
||||
placeholder: propTypes.any.isRequired,
|
||||
required: propTypes.bool,
|
||||
value: propTypes.any,
|
||||
xoContainers: propTypes.array,
|
||||
xoObjects: propTypes.oneOfType([
|
||||
propTypes.array,
|
||||
propTypes.objectOf(propTypes.array),
|
||||
]).isRequired,
|
||||
})
|
||||
export class GenericSelect extends Component {
|
||||
class GenericSelect extends React.Component {
|
||||
static propTypes = {
|
||||
hasSelectAll: PropTypes.bool,
|
||||
multi: PropTypes.bool,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
xoContainers: PropTypes.array,
|
||||
xoObjects: PropTypes.oneOfType([
|
||||
PropTypes.array,
|
||||
PropTypes.objectOf(PropTypes.array),
|
||||
]).isRequired,
|
||||
}
|
||||
|
||||
_getObjectsById = createSelector(
|
||||
() => this.props.xoObjects,
|
||||
objects =>
|
||||
@ -142,8 +135,8 @@ export class GenericSelect extends Component {
|
||||
// createCollectionWrapper with a depth?
|
||||
const { name } = this.constructor
|
||||
|
||||
let options = []
|
||||
if (!containers) {
|
||||
let options
|
||||
if (containers === undefined) {
|
||||
if (__DEV__ && !isArray(objects)) {
|
||||
throw new Error(
|
||||
`${name}: without xoContainers, xoObjects must be an array`
|
||||
@ -151,27 +144,29 @@ export class GenericSelect extends Component {
|
||||
}
|
||||
|
||||
options = map(objects, getOption)
|
||||
} else if (__DEV__ && isArray(objects)) {
|
||||
throw new Error(
|
||||
`${name}: with xoContainers, xoObjects must be an object`
|
||||
)
|
||||
} else {
|
||||
if (__DEV__ && isArray(objects)) {
|
||||
throw new Error(
|
||||
`${name}: with xoContainers, xoObjects must be an object`
|
||||
)
|
||||
}
|
||||
|
||||
options = []
|
||||
forEach(containers, container => {
|
||||
options.push({
|
||||
disabled: true,
|
||||
xoItem: container,
|
||||
})
|
||||
|
||||
forEach(objects[container.id], object => {
|
||||
options.push(getOption(object, container))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
forEach(containers, container => {
|
||||
options.push({
|
||||
disabled: true,
|
||||
xoItem: container,
|
||||
})
|
||||
|
||||
forEach(objects[container.id], object => {
|
||||
options.push(getOption(object, container))
|
||||
})
|
||||
})
|
||||
|
||||
const values = this._getSelectValue()
|
||||
const objectsById = this._getObjectsById()
|
||||
const addIfMissing = val => {
|
||||
if (val && !objectsById[val]) {
|
||||
if (val != null && !(val in objectsById)) {
|
||||
options.push({
|
||||
disabled: true,
|
||||
id: val,
|
||||
@ -185,6 +180,7 @@ export class GenericSelect extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
const values = this._getSelectedIds()
|
||||
if (isArray(values)) {
|
||||
forEach(values, addIfMissing)
|
||||
} else {
|
||||
@ -195,27 +191,25 @@ export class GenericSelect extends Component {
|
||||
}
|
||||
)
|
||||
|
||||
_getSelectValue = createSelector(
|
||||
_getSelectedIds = createSelector(
|
||||
() => this.props.value,
|
||||
createCollectionWrapper(getIds)
|
||||
)
|
||||
|
||||
_getNewSelectedObjects = createSelector(
|
||||
this._getObjectsById,
|
||||
value => value,
|
||||
(objectsById, value) =>
|
||||
value == null
|
||||
? value
|
||||
: isArray(value)
|
||||
_getSelectedObjects = (() => {
|
||||
const helper = createSelector(
|
||||
this._getObjectsById,
|
||||
value => value,
|
||||
(objectsById, value) =>
|
||||
isArray(value)
|
||||
? map(value, value => objectsById[value.value])
|
||||
: objectsById[value.value]
|
||||
)
|
||||
)
|
||||
return value => (value == null ? value : helper(value))
|
||||
})()
|
||||
|
||||
_onChange = value => {
|
||||
const { onChange } = this.props
|
||||
if (onChange) {
|
||||
onChange(this._getNewSelectedObjects(value))
|
||||
}
|
||||
this.props.onChange(this._getSelectedObjects(value))
|
||||
}
|
||||
|
||||
_selectAll = () => {
|
||||
@ -225,46 +219,31 @@ export class GenericSelect extends Component {
|
||||
// GroupBy: Display option with margin if not disabled and containers exists.
|
||||
_renderOption = option => (
|
||||
<span
|
||||
className={classNames(
|
||||
!option.disabled && this.props.xoContainers && 'ml-1'
|
||||
)}
|
||||
className={
|
||||
!option.disabled && this.props.xoContainers !== undefined
|
||||
? 'ml-1'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{renderXoItem(option.xoItem)}
|
||||
</span>
|
||||
)
|
||||
|
||||
render () {
|
||||
const {
|
||||
autoFocus,
|
||||
disabled,
|
||||
hasSelectAll,
|
||||
multi,
|
||||
placeholder,
|
||||
required,
|
||||
|
||||
clearable = Boolean(multi || !required),
|
||||
} = this.props
|
||||
const { hasSelectAll, xoContainers, xoObjects, ...props } = this.props
|
||||
|
||||
const select = (
|
||||
<Select
|
||||
{...{
|
||||
autoFocus,
|
||||
clearable,
|
||||
disabled,
|
||||
multi,
|
||||
placeholder,
|
||||
required,
|
||||
}}
|
||||
{...props}
|
||||
onChange={this._onChange}
|
||||
openOnFocus
|
||||
optionRenderer={this._renderOption}
|
||||
options={this._getOptions()}
|
||||
value={this._getSelectValue()}
|
||||
valueRenderer={this._renderOption}
|
||||
value={this._getSelectedIds()}
|
||||
/>
|
||||
)
|
||||
|
||||
if (!multi || !hasSelectAll) {
|
||||
if (!props.multi || !hasSelectAll) {
|
||||
return select
|
||||
}
|
||||
|
||||
@ -295,36 +274,34 @@ const makeStoreSelect = (createSelectors, defaultProps) =>
|
||||
|
||||
const makeSubscriptionSelect = (subscribe, props) =>
|
||||
uncontrollableInput(options)(
|
||||
class extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
class extends React.PureComponent {
|
||||
state = {}
|
||||
|
||||
this._getFilteredXoContainers = createFilter(
|
||||
() => this.state.xoContainers,
|
||||
() => this.props.containerPredicate
|
||||
)
|
||||
_getFilteredXoContainers = createFilter(
|
||||
() => this.state.xoContainers,
|
||||
() => this.props.containerPredicate
|
||||
)
|
||||
|
||||
this._getFilteredXoObjects = createSelector(
|
||||
() => this.state.xoObjects,
|
||||
() => this.state.xoContainers && this._getFilteredXoContainers(),
|
||||
() => this.props.predicate,
|
||||
(xoObjects, xoContainers, predicate) => {
|
||||
if (xoContainers == null) {
|
||||
return filter(xoObjects, predicate)
|
||||
} else {
|
||||
// Filter xoObjects with `predicate`...
|
||||
const filteredObjects = mapValues(xoObjects, xoObjectsGroup =>
|
||||
filter(xoObjectsGroup, predicate)
|
||||
)
|
||||
// ...and keep only those whose xoContainer hasn't been filtered out
|
||||
return pick(
|
||||
filteredObjects,
|
||||
map(xoContainers, container => container.id)
|
||||
)
|
||||
}
|
||||
_getFilteredXoObjects = createSelector(
|
||||
() => this.state.xoObjects,
|
||||
() => this.state.xoContainers && this._getFilteredXoContainers(),
|
||||
() => this.props.predicate,
|
||||
(xoObjects, xoContainers, predicate) => {
|
||||
if (xoContainers == null) {
|
||||
return filter(xoObjects, predicate)
|
||||
} else {
|
||||
// Filter xoObjects with `predicate`...
|
||||
const filteredObjects = mapValues(xoObjects, xoObjectsGroup =>
|
||||
filter(xoObjectsGroup, predicate)
|
||||
)
|
||||
// ...and keep only those whose xoContainer hasn't been filtered out
|
||||
return pick(
|
||||
filteredObjects,
|
||||
map(xoContainers, container => container.id)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
componentWillMount () {
|
||||
this.componentWillUnmount = subscribe(::this.setState)
|
||||
@ -577,36 +554,35 @@ export const SelectHighLevelObject = makeStoreSelect(
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export const SelectVdi = propTypes({
|
||||
srPredicate: propTypes.func,
|
||||
})(
|
||||
makeStoreSelect(
|
||||
() => {
|
||||
const getSrs = createGetObjectsOfType('SR').filter(
|
||||
(_, props) => props.srPredicate
|
||||
)
|
||||
const getVdis = createGetObjectsOfType('VDI')
|
||||
.filter(
|
||||
createSelector(
|
||||
getSrs,
|
||||
getPredicate,
|
||||
(srs, predicate) =>
|
||||
predicate
|
||||
? vdi => srs[vdi.$SR] && predicate(vdi)
|
||||
: vdi => srs[vdi.$SR]
|
||||
)
|
||||
export const SelectVdi = makeStoreSelect(
|
||||
() => {
|
||||
const getSrs = createGetObjectsOfType('SR').filter(
|
||||
(_, props) => props.srPredicate
|
||||
)
|
||||
const getVdis = createGetObjectsOfType('VDI')
|
||||
.filter(
|
||||
createSelector(
|
||||
getSrs,
|
||||
getPredicate,
|
||||
(srs, predicate) =>
|
||||
predicate
|
||||
? vdi => srs[vdi.$SR] && predicate(vdi)
|
||||
: vdi => srs[vdi.$SR]
|
||||
)
|
||||
.sort()
|
||||
.groupBy('$SR')
|
||||
)
|
||||
.sort()
|
||||
.groupBy('$SR')
|
||||
|
||||
return {
|
||||
xoObjects: getVdis,
|
||||
xoContainers: getSrs.sort(),
|
||||
}
|
||||
},
|
||||
{ placeholder: _('selectVdis') }
|
||||
)
|
||||
return {
|
||||
xoObjects: getVdis,
|
||||
xoContainers: getSrs.sort(),
|
||||
}
|
||||
},
|
||||
{ placeholder: _('selectVdis') }
|
||||
)
|
||||
SelectVdi.propTypes = {
|
||||
srPredicate: PropTypes.func,
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
|
||||
@ -747,7 +723,7 @@ export const SelectResourceSet = makeSubscriptionSelect(
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export class SelectResourceSetsVmTemplate extends Component {
|
||||
export class SelectResourceSetsVmTemplate extends React.PureComponent {
|
||||
get value () {
|
||||
return this.refs.select.value
|
||||
}
|
||||
@ -782,7 +758,7 @@ export class SelectResourceSetsVmTemplate extends Component {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export class SelectResourceSetsSr extends Component {
|
||||
export class SelectResourceSetsSr extends React.PureComponent {
|
||||
get value () {
|
||||
return this.refs.select.value
|
||||
}
|
||||
@ -813,7 +789,7 @@ export class SelectResourceSetsSr extends Component {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export class SelectResourceSetsVdi extends Component {
|
||||
export class SelectResourceSetsVdi extends React.PureComponent {
|
||||
get value () {
|
||||
return this.refs.select.value
|
||||
}
|
||||
@ -853,7 +829,7 @@ export class SelectResourceSetsVdi extends Component {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export class SelectResourceSetsNetwork extends Component {
|
||||
export class SelectResourceSetsNetwork extends React.PureComponent {
|
||||
get value () {
|
||||
return this.refs.select.value
|
||||
}
|
||||
@ -894,12 +870,13 @@ export class SelectResourceSetsNetwork extends Component {
|
||||
ipPools: subscribeIpPools,
|
||||
resourceSets: subscribeResourceSets,
|
||||
}))
|
||||
@propTypes({
|
||||
containerPredicate: propTypes.func,
|
||||
predicate: propTypes.func,
|
||||
resourceSetId: propTypes.string.isRequired,
|
||||
})
|
||||
export class SelectResourceSetIp extends Component {
|
||||
export class SelectResourceSetIp extends React.Component {
|
||||
static propTypes = {
|
||||
containerPredicate: PropTypes.func,
|
||||
predicate: PropTypes.func,
|
||||
resourceSetId: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
get value () {
|
||||
return this.refs.select.value
|
||||
}
|
||||
@ -958,7 +935,7 @@ export class SelectResourceSetIp extends Component {
|
||||
|
||||
// ===================================================================
|
||||
|
||||
export class SelectSshKey extends Component {
|
||||
export class SelectSshKey extends React.PureComponent {
|
||||
get value () {
|
||||
return this.refs.select.value
|
||||
}
|
||||
|
@ -5,12 +5,12 @@ import endsWith from 'lodash/endsWith'
|
||||
import Icon from 'icon'
|
||||
import React from 'react'
|
||||
import replace from 'lodash/replace'
|
||||
import Select from 'form/select'
|
||||
import Tooltip from 'tooltip'
|
||||
import { Container, Col, Row } from 'grid'
|
||||
import { createSelector } from 'reselect'
|
||||
import { formatSize } from 'utils'
|
||||
import { FormattedDate } from 'react-intl'
|
||||
import { SelectPlainObject } from 'form'
|
||||
import { filter, includes, isEmpty, map } from 'lodash'
|
||||
import { scanDisk, scanFiles } from 'xo'
|
||||
|
||||
@ -251,23 +251,25 @@ export default class RestoreFileModalBody extends Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SelectPlainObject
|
||||
<Select
|
||||
labelKey='name'
|
||||
onChange={this._onBackupChange}
|
||||
optionKey='id'
|
||||
optionRenderer={backupOptionRenderer}
|
||||
options={backups}
|
||||
placeholder={_('restoreFilesSelectBackup')}
|
||||
value={backup}
|
||||
valueKey='id'
|
||||
/>
|
||||
{backup && [
|
||||
<br />,
|
||||
<SelectPlainObject
|
||||
<Select
|
||||
labelKey='name'
|
||||
onChange={this._onDiskChange}
|
||||
optionKey='id'
|
||||
optionRenderer={diskOptionRenderer}
|
||||
options={backup.disks}
|
||||
placeholder={_('restoreFilesSelectDisk')}
|
||||
value={disk}
|
||||
valueKey='id'
|
||||
/>,
|
||||
]}
|
||||
{scanDiskError && (
|
||||
@ -279,13 +281,14 @@ export default class RestoreFileModalBody extends Component {
|
||||
!scanDiskError &&
|
||||
!noPartitions && [
|
||||
<br />,
|
||||
<SelectPlainObject
|
||||
<Select
|
||||
labelKey='name'
|
||||
onChange={this._onPartitionChange}
|
||||
optionKey='id'
|
||||
optionRenderer={partitionOptionRenderer}
|
||||
options={partitions}
|
||||
placeholder={_('restoreFilesSelectPartition')}
|
||||
value={partition}
|
||||
valueKey='id'
|
||||
/>,
|
||||
]}
|
||||
{(partition || (disk && !scanDiskError && noPartitions)) && [
|
||||
@ -311,13 +314,14 @@ export default class RestoreFileModalBody extends Component {
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>,
|
||||
<SelectPlainObject
|
||||
<Select
|
||||
labelKey='name'
|
||||
onChange={this._onFileChange}
|
||||
optionKey='id'
|
||||
optionRenderer={fileOptionRenderer}
|
||||
options={this._getSelectableFiles()}
|
||||
placeholder={_('restoreFilesSelectFiles')}
|
||||
value={null}
|
||||
valueKey='id'
|
||||
/>,
|
||||
<br />,
|
||||
<div>
|
||||
|
@ -22,7 +22,7 @@ import { addSubscriptions, noop } from 'utils'
|
||||
import { Container, Row, Col } from 'grid'
|
||||
import { FormattedDate, injectIntl } from 'react-intl'
|
||||
import { info, error } from 'notification'
|
||||
import { SelectPlainObject, Toggle } from 'form'
|
||||
import { Select, Toggle } from 'form'
|
||||
|
||||
import {
|
||||
importBackup,
|
||||
@ -210,14 +210,15 @@ class _ModalBody extends Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<SelectPlainObject
|
||||
<Select
|
||||
labelKey='name'
|
||||
onChange={this.linkState('backup')}
|
||||
optionKey='path'
|
||||
optionRenderer={backupOptionRenderer}
|
||||
options={props.backups}
|
||||
placeholder={props.intl.formatMessage(
|
||||
messages.importBackupModalSelectBackup
|
||||
)}
|
||||
valueKey='path'
|
||||
/>
|
||||
<br />
|
||||
<ChooseSrForEachVdisModal
|
||||
|
@ -3,6 +3,7 @@ import ActionButton from 'action-button'
|
||||
import ActionRowButton from 'action-row-button'
|
||||
import Button from 'button'
|
||||
import Component from 'base-component'
|
||||
import defined from 'xo-defined'
|
||||
import delay from 'lodash/delay'
|
||||
import find from 'lodash/find'
|
||||
import forEach from 'lodash/forEach'
|
||||
@ -13,6 +14,7 @@ import isEmpty from 'lodash/isEmpty'
|
||||
import map from 'lodash/map'
|
||||
import mapValues from 'lodash/mapValues'
|
||||
import React from 'react'
|
||||
import Select from 'form/select'
|
||||
import size from 'lodash/size'
|
||||
import Tooltip from 'tooltip'
|
||||
import Upgrade from 'xoa-upgrade'
|
||||
@ -21,7 +23,6 @@ import { createSelector } from 'selectors'
|
||||
import { error } from 'notification'
|
||||
import { generateUiSchema } from 'xo-json-schema-input'
|
||||
import { injectIntl } from 'react-intl'
|
||||
import { SelectPlainObject } from 'form'
|
||||
import { SelectSubject } from 'select-objects'
|
||||
|
||||
import {
|
||||
@ -119,17 +120,7 @@ export default class Jobs extends Component {
|
||||
})
|
||||
}
|
||||
|
||||
componentWillReceiveProps (props) {
|
||||
const { currentUser } = props
|
||||
const { owner } = this.state
|
||||
|
||||
if (currentUser && !owner) {
|
||||
this.setState({ owner: currentUser.id })
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
this.setState({ owner: this.props.user && this.props.user.id })
|
||||
this.componentWillUnmount = subscribeJobs(jobs => {
|
||||
const j = {}
|
||||
for (const id in jobs) {
|
||||
@ -279,7 +270,7 @@ export default class Jobs extends Component {
|
||||
params.value
|
||||
),
|
||||
},
|
||||
userId: owner,
|
||||
userId: owner !== undefined ? owner : this.props.currentUser.id,
|
||||
timeout: timeout ? timeout * 1e3 : undefined,
|
||||
}
|
||||
|
||||
@ -372,8 +363,8 @@ export default class Jobs extends Component {
|
||||
type === 'user' && permission === 'admin'
|
||||
|
||||
render () {
|
||||
const { state } = this
|
||||
const { action, actions, job, jobs, owner } = state
|
||||
const { props, state } = this
|
||||
const { action, actions, job, jobs } = state
|
||||
const { formatMessage } = this.props.intl
|
||||
|
||||
const isJobUserMissing = this._getIsJobUserMissing()
|
||||
@ -387,7 +378,7 @@ export default class Jobs extends Component {
|
||||
placeholder={_('jobOwnerPlaceholder')}
|
||||
predicate={this._subjectPredicate}
|
||||
required
|
||||
value={owner}
|
||||
value={defined(state.owner, () => props.currentUser.id)}
|
||||
/>
|
||||
<input
|
||||
type='text'
|
||||
@ -397,12 +388,13 @@ export default class Jobs extends Component {
|
||||
pattern='[^_]+'
|
||||
required
|
||||
/>
|
||||
<SelectPlainObject
|
||||
ref='method'
|
||||
options={actions}
|
||||
optionKey='method'
|
||||
<Select
|
||||
labelKey='method'
|
||||
onChange={this._handleSelectMethod}
|
||||
options={actions}
|
||||
placeholder={_('jobActionPlaceHolder')}
|
||||
ref='method'
|
||||
valueKey='method'
|
||||
/>
|
||||
<input
|
||||
type='number'
|
||||
|
@ -10,7 +10,7 @@ import React, { Component } from 'react'
|
||||
import Scheduler, { SchedulePreview } from 'scheduling'
|
||||
import { error } from 'notification'
|
||||
import { injectIntl } from 'react-intl'
|
||||
import { SelectPlainObject, Toggle } from 'form'
|
||||
import { Select, Toggle } from 'form'
|
||||
import {
|
||||
createSchedule,
|
||||
deleteSchedule,
|
||||
@ -223,10 +223,11 @@ export default class Schedules extends Component {
|
||||
/>
|
||||
</div>
|
||||
<div className='form-group'>
|
||||
<SelectPlainObject
|
||||
<Select
|
||||
labelKey='name'
|
||||
ref='job'
|
||||
options={map(jobs)}
|
||||
optionKey='id'
|
||||
valueKey='id'
|
||||
placeholder={this.props.intl.formatMessage(
|
||||
messages.jobScheduleJobPlaceHolder
|
||||
)}
|
||||
|
@ -381,6 +381,7 @@ export class Edit extends Component {
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<SelectSubject
|
||||
autoSelectSingleOption={false}
|
||||
hasSelectAll
|
||||
multi
|
||||
onChange={this.linkState('subjects')}
|
||||
@ -390,6 +391,7 @@ export class Edit extends Component {
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<SelectPool
|
||||
autoSelectSingleOption={false}
|
||||
hasSelectAll
|
||||
multi
|
||||
onChange={this._updateSelectedPools}
|
||||
@ -414,6 +416,7 @@ export class Edit extends Component {
|
||||
<Row>
|
||||
<Col mediumSize={4}>
|
||||
<SelectVmTemplate
|
||||
autoSelectSingleOption={false}
|
||||
disabled={!state.nPools}
|
||||
hasSelectAll
|
||||
multi
|
||||
@ -425,6 +428,7 @@ export class Edit extends Component {
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<SelectSr
|
||||
autoSelectSingleOption={false}
|
||||
disabled={!state.nPools}
|
||||
hasSelectAll
|
||||
multi
|
||||
@ -436,6 +440,7 @@ export class Edit extends Component {
|
||||
</Col>
|
||||
<Col mediumSize={4}>
|
||||
<SelectNetwork
|
||||
autoSelectSingleOption={false}
|
||||
disabled={!state.nSrs}
|
||||
hasSelectAll
|
||||
multi
|
||||
@ -535,8 +540,8 @@ export class Edit extends Component {
|
||||
<Col mediumSize={7}>
|
||||
<SelectIpPool
|
||||
onChange={this._onChangeIpPool}
|
||||
value=''
|
||||
predicate={this._getIpPoolPredicate()}
|
||||
value={null}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
@ -166,7 +166,7 @@ export default class Acls extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
action: '',
|
||||
action: null,
|
||||
objects: [],
|
||||
subjects: [],
|
||||
typeFilters: {},
|
||||
|
11
yarn.lock
11
yarn.lock
@ -1270,7 +1270,7 @@ babel-runtime@^5.8.25:
|
||||
dependencies:
|
||||
core-js "^1.0.0"
|
||||
|
||||
babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0:
|
||||
babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
dependencies:
|
||||
@ -7292,14 +7292,15 @@ react-transition-group@^2.2.0:
|
||||
prop-types "^15.5.8"
|
||||
warning "^3.0.0"
|
||||
|
||||
react-virtualized@^8.0.8:
|
||||
version "8.11.4"
|
||||
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-8.11.4.tgz#0bb94f1ecbd286d07145ce63983d0a11724522c0"
|
||||
react-virtualized@^9.15.0:
|
||||
version "9.17.3"
|
||||
resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.17.3.tgz#3854588f067235c00ae5cd134d96508956630033"
|
||||
dependencies:
|
||||
babel-runtime "^6.11.6"
|
||||
babel-runtime "^6.26.0"
|
||||
classnames "^2.2.3"
|
||||
dom-helpers "^2.4.0 || ^3.0.0"
|
||||
loose-envify "^1.3.0"
|
||||
prop-types "^15.5.4"
|
||||
|
||||
react@^15.4.1:
|
||||
version "15.6.2"
|
||||
|
Loading…
Reference in New Issue
Block a user