)
}
});
@@ -20,4 +26,4 @@ var Example = React.createClass({
ReactDOM.render(, mountNode);`
return code
-}
\ No newline at end of file
+}
diff --git a/docs/components/pages/Multiselect.api.md b/docs/components/pages/Multiselect.api.md
index 5b2d91faf..2edc7fa68 100644
--- a/docs/components/pages/Multiselect.api.md
+++ b/docs/components/pages/Multiselect.api.md
@@ -18,12 +18,12 @@ change event Handler that is called when the value is changed. The handler is ca
### onSelect?{ type:'Function(Any value)'}
-This handler fires when an item has been selected from the list. It fires before the `onChange` handler, and fires
+This handler fires when an item has been selected from the list. It fires before the `onChange` handler, and fires
regardless of whether the value has actually changed.
### onCreate?{ type: 'Function(String searchTerm)'}
-This handler fires when the user chooses to create a new tag, not in the data list. It is up to the widget parent to implement creation logic,
+This handler fires when the user chooses to create a new tag, not in the data list. It is up to the widget parent to implement creation logic,
a common implementation is shown below, where the new tag is selected and added to the data list.
@@ -36,18 +36,18 @@ properties comprise the value field (such as an id) and the field used to label
### valueField?{ type: 'String' }
-A dataItem field name for uniquely identifying items in the `data` list. A `valueField` is required
+A dataItem field name for uniquely identifying items in the `data` list. A `valueField` is required
when the `value` prop is not itself a dataItem. A `valueField` is useful when specifying the selected item, by
its `id` instead of using the model as the value.
-When a `valueField` is not provided, the {widgetName} will use strict equality checks (`===`) to locate
+When a `valueField` is not provided, the {widgetName} will use strict equality checks (`===`) to locate
the `value` in the `data` list.
### textField?{ type: 'String | Function(dataItem)' }
-Specify which data item field to display in the ${widgetName} and selected item. The `textField` prop
+Specify which data item field to display in the ${widgetName} and selected item. The `textField` prop
may also also used as to find an item in the list as you type. Providing an accessor function allows for computed text values
@@ -68,14 +68,14 @@ renders the text of the selected item (specified by `textfield`)
### groupBy?{ type: 'String | Function(Any dataItem)' }
-Determines how to group the {widgetName} dropdown list. Providing a `string` will group
+Determines how to group the {widgetName} values. Providing a `string` will group
the `data` array by that property. You can also provide a 'function' which should return the group value.
### groupComponent?{ type: 'Component' }
-This component is used to render each option group, when `groupBy` is specified. By
+This component is used to render each option group, when `groupBy` is specified. By
default the `groupBy` value will be used.
@@ -93,7 +93,7 @@ The `defaultSearchTerm` prop can be used to set an initialization value for unco
### onSearch?{ type: 'Function(String searchTerm)' }
-Called when the value of the text box changes either from typing or a pasted value. `onSearch` should
+Called when the value of the text box changes either from typing or a pasted value. `onSearch` should
be used when the `searchTerm` prop is set.
### open?{ type: 'Boolean' }
@@ -110,8 +110,8 @@ when the `open` prop is set otherwise the widget will never open or close.
### filter?{ type: '[String, Function(dataItem, searchTerm)]', default: 'startsWith' }
-Specify a filtering method used to reduce the items in the dropdown as you type. There are a few prebuilt filtering
-methods that can be specified by passing the `String` name. You can explicitly opt out of filtering by
+Specify a filtering method used to reduce the items in the dropdown as you type. There are a few built-in filtering
+methods that can be specified by passing the `String` name. You can explicitly opt out of filtering by
setting filter to `false`
To handle custom filtering techniques provide a `function` that returns `true` or `false` for each passed in item
@@ -150,11 +150,11 @@ Place the widget in a readonly mode, If an `Array` of values is passed in only t
### isRtl?{ type: 'Boolean', default:"false" }
-mark whether the widget should render right-to-left. This property can also be implicitly passed to the widget through
+mark whether the widget should render right-to-left. This property can also be implicitly passed to the widget through
a `childContext` prop (`isRtl`) this allows higher level application components to specify the direction.
-### messages?{ type: 'Object' }
+### messages?{ type: 'Object' }
Object hash containing display text and/or text for screen readers. Use the `messages` object to
localize widget text and increase accessibility.
diff --git a/docs/components/pages/SelectList.api.md b/docs/components/pages/SelectList.api.md
index 221bf39f3..125127bad 100644
--- a/docs/components/pages/SelectList.api.md
+++ b/docs/components/pages/SelectList.api.md
@@ -12,7 +12,7 @@ the `data` array; widgets can have values that are not in their list.
### onChange?{ type: 'Function(Array|Any values)' }
-Change event handler that is called when the value is changed. `values` will be an array
+Change event handler that is called when the value is changed. `values` will be an array
when `multiple` prop is set.
@@ -25,21 +25,27 @@ properties comprise the value field (such as an id) and the field used to label
### valueField?{ type: 'String' }
-A dataItem field name for uniquely identifying items in the `data` list. A `valueField` is required
+A dataItem field name for uniquely identifying items in the `data` list. A `valueField` is required
when the `value` prop is not itself a dataItem. A `valueField` is useful when specifying the selected item, by
its `id` instead of using the model as the value.
-When a `valueField` is not provided, the {widgetName} will use strict equality checks (`===`) to locate
+When a `valueField` is not provided, the {widgetName} will use strict equality checks (`===`) to locate
the `value` in the `data` list.
### textField?{ type: 'String | Function(dataItem)' }
-Specify which data item field to display in the ${widgetName} and selected item. The `textField` prop may also also used as to find an item in the list as you type. Providing an accessor function allows for computed text values
+Specify which data item field to display in the ${widgetName} and selected item. The `textField` prop may also also
+used as to find an item in the list as you type. Providing an accessor function allows for computed text values
+### multiple?{ type: 'Boolean' }
+
+Whether or not the {widgetName} allows multiple selection or not. when `false` the {widgetName} will
+render as a list of radio buttons, and checkboxes when `true`.
+
### itemComponent?{ type: 'Component' }
This component is used to render each item in the {widgetName}. The default component
@@ -47,17 +53,26 @@ renders the text of the selected item (specified by `textfield`)
-### multiple?{ type: 'Boolean' }
+### groupBy?{ type: 'String | Function(Any dataItem)' }
-Whether or not the {widgetName} allows multiple selection or not. when `false` the {widgetName} will
-render as a list of radio buttons, and checkboxes when `true`.
+Determines how to group the {widgetName} dropdown list. Providing a `string` will group
+the `data` array by that property. You can also provide a 'function' which should return the group value.
+
+
+
+### groupComponent?{ type: 'Component' }
+
+This component is used to render each option group, when `groupBy` is specified. By
+default the `groupBy` value will be used.
+
+
### onMove?{ type: 'Function(HTMLElement list, HTMLElement focusedNode, Any focusedItem)' }
A handler called when focus shifts on the {widgetName}. Internally this is used to ensure the focused item is in view.
-If you want to define your own "scrollTo" behavior or just disable the default one specify an `onMove` handler.
-The handler is called with the relevant DOM nodes needed to implement scroll behavior: the list element,
+If you want to define your own "scrollTo" behavior or just disable the default one specify an `onMove` handler.
+The handler is called with the relevant DOM nodes needed to implement scroll behavior: the list element,
the element that is currently focused, and a focused value.
@@ -74,15 +89,29 @@ Disable the widget, if an `Array` of values is passed in only those values will
### readOnly?{ type: '[Boolean, Array]' }
-Place the {widgetName} in a readonly mode, If an `Array` of values is passed in only those values will be readonly.
+Place the {widgetName} in a read-only mode, If an `Array` of values is passed in only those values will be read-only.
+### groupBy?{ type: 'String | Function(Any dataItem)' }
+
+Determines how to group the {widgetName}. Providing a `string` will group
+the `data` array by that property. You can also provide a function which should return the group value.
+
+
+
+### groupComponent?{ type: 'Component' }
+
+This component is used to render each option group, when `groupBy` is specified. By
+default the `groupBy` value will be used.
+
+
+
### isRtl?{ type: 'Boolean', default:"false" }
mark whether the {widgetName} should render right-to-left. This property can also be implicitly passed to the widget through a `childContext` prop (`isRtl`) this allows higher level application components to specify the direction.
-### messages?{ type: 'Object' }
+### messages?{ type: 'Object' }
Object hash containing display text and/or text for screen readers. Use the `messages` object to
localize widget text and increase accessibility.
diff --git a/src/SelectList.jsx b/src/SelectList.jsx
index 217566225..dda23fd19 100644
--- a/src/SelectList.jsx
+++ b/src/SelectList.jsx
@@ -6,6 +6,8 @@ import compat from './util/compat';
import CustomPropTypes from './util/propTypes';
import PlainList from './List';
+import GroupableList from './ListGroupable';
+import ListOption from './ListOption';
import validateList from './util/validateListInterface';
import scrollTo from 'dom-helpers/util/scrollTo';
@@ -14,10 +16,7 @@ import { dataItem } from './util/dataHelpers';
import { widgetEditable, widgetEnabled } from './util/interaction';
import { instanceId, notify } from './util/widgetHelpers';
-import {
- move, contains
- , isDisabled, isReadOnly
- , isDisabledItem, isReadOnlyItem } from './util/interaction';
+import { isDisabled, isReadOnly, contains } from './util/interaction';
let { omit, pick } = _;
@@ -53,6 +52,7 @@ let propTypes = {
}
+
var SelectList = React.createClass({
propTypes: propTypes,
@@ -109,10 +109,10 @@ var SelectList = React.createClass({
render() {
let {
- className, tabIndex, busy
+ className, tabIndex, busy, groupBy
, listComponent: List } = this.props;
- List = List || PlainList
+ List = List || (groupBy && GroupableList) || PlainList
let elementProps = omit(this.props, Object.keys(propTypes));
let listProps = pick(this.props, Object.keys(List.propTypes));
@@ -178,11 +178,8 @@ var SelectList = React.createClass({
var key = e.key
, { valueField, multiple } = this.props
, list = this.refs.list
- , focusedItem = this.state.focusedItem
- , props = this.props;
-
+ , focusedItem = this.state.focusedItem;
- let moveItem = (dir, item)=> move(dir, item, props, list);
let change = (item) => {
if (item)
this._change(item, multiple
@@ -198,14 +195,14 @@ var SelectList = React.createClass({
if (key === 'End') {
e.preventDefault()
- if (multiple) this.setState({ focusedItem: moveItem('prev', null) })
- else change(moveItem('prev', null))
+ if (multiple) this.setState({ focusedItem: list.last() })
+ else change(list.last())
}
else if (key === 'Home' ) {
e.preventDefault()
- if (multiple) this.setState({ focusedItem: moveItem('next', null) })
- else change(moveItem('next', null))
+ if (multiple) this.setState({ focusedItem: list.first() })
+ else change(list.first())
}
else if (key === 'Enter' || key === ' ' ) {
e.preventDefault()
@@ -214,14 +211,14 @@ var SelectList = React.createClass({
else if (key === 'ArrowDown' || key === 'ArrowRight' ) {
e.preventDefault()
- if (multiple) this.setState({ focusedItem: moveItem('next', focusedItem) })
- else change(moveItem('next', focusedItem))
+ if (multiple) this.setState({ focusedItem: list.next(focusedItem) })
+ else change(list.next(focusedItem))
}
else if (key === 'ArrowUp' || key === 'ArrowLeft' ) {
e.preventDefault()
- if (multiple) this.setState({ focusedItem: moveItem('prev', focusedItem) })
- else change(moveItem('prev', focusedItem))
+ if (multiple) this.setState({ focusedItem: list.prev(focusedItem) })
+ else change(list.prev(focusedItem))
}
else if (multiple && e.keyCode === 65 && e.ctrlKey ) {
e.preventDefault()
@@ -318,31 +315,21 @@ function getListItem(parent){
render() {
let {
- children, focused, selected
- , dataItem: item
- , ...props } = this.props;
+ children
+ , disabled, readonly
+ , dataItem: item } = this.props;
let { multiple, name = instanceId(parent, '_name') } = parent.props;
- let checked = contains(item, parent._values(), parent.props.valueField)
- , change = parent._change.bind(null, item)
- , disabled = isDisabledItem(item, parent.props)
- , readonly = isReadOnlyItem(item, parent.props)
+ let checked = contains(item, parent._values(), parent.props.valueField)
+ , change = parent._change.bind(null, item)
, type = multiple ? 'checkbox' : 'radio';
return (
-
-
+
);
function onChange(e){
diff --git a/src/less/selectlist.less b/src/less/selectlist.less
index a2b002f23..c84cda5fb 100644
--- a/src/less/selectlist.less
+++ b/src/less/selectlist.less
@@ -11,7 +11,7 @@
overflow: auto;
> li {
-
+
&.rw-list-option {
position: relative;
min-height: 27px;
@@ -22,7 +22,7 @@
position: absolute;
margin: 4px 0 0 -20px;
}
-
+
> label {
padding-left: 20px;
line-height: 1.423em;
@@ -62,14 +62,11 @@
}
.rw-selectlist {
-
&.rw-state-disabled,
&.rw-state-readonly {
-
> ul > li:hover {
background: none;
border-color: transparent;
}
}
-
-}
\ No newline at end of file
+}
diff --git a/src/util/interaction.js b/src/util/interaction.js
index a95f5564a..37f6ac679 100644
--- a/src/util/interaction.js
+++ b/src/util/interaction.js
@@ -29,7 +29,7 @@ export function move(dir, item, props, list) {
, stop = dir === 'next' ? list.last() : list.first()
, next = list[dir](item);
- while( next !== stop && isDisabledOrReadonly(next))
+ while (next !== stop && isDisabledOrReadonly(next))
next = list[dir](next)
return isDisabledOrReadonly(next) ? item : next