From 1058e3f51e63557fceae22b218f1214c5cf69830 Mon Sep 17 00:00:00 2001 From: jquense Date: Tue, 3 Nov 2015 00:43:03 +0700 Subject: [PATCH] [added] disable individual dropdown/combobox options --- docs/components/pages/ComboBox.api.md | 30 +++++--- docs/components/pages/DropdownList.api.md | 36 ++++++--- src/Combobox.jsx | 10 ++- src/DropdownList.jsx | 11 +-- src/List.jsx | 22 ++++-- src/ListGroupable.jsx | 21 ++++-- src/ListOption.jsx | 12 ++- src/less/core.less | 16 ++-- src/mixins/ListMovementMixin.js | 92 +++++++++++------------ 9 files changed, 146 insertions(+), 104 deletions(-) diff --git a/docs/components/pages/ComboBox.api.md b/docs/components/pages/ComboBox.api.md index 6d3448424..ec24347ad 100644 --- a/docs/components/pages/ComboBox.api.md +++ b/docs/components/pages/ComboBox.api.md @@ -21,7 +21,7 @@ the string value of the {widgetName} will be returned. ### 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. @@ -29,45 +29,57 @@ regardless of whether the value has actually changed. ### data?{ type: 'Array' } An array of possible values for the {widgetName}. If an array of `objects` is provided you -should usethe `valueField` and `textField` props, to specify which object +should use the `valueField` and `textField` props, to specify which object properties comprise the value field (such as an id) and the field used to label the item. ### 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 ### itemComponent?{ type: 'Component' } -This component is used to render each possible item in the DropdownList. The default component +This component is used to render each possible item in the ${widgetName}. The default component renders the text of the selected item (specified by `textfield`) +### disabled?{ type: '[Boolean, Array]' } + +Disable the widget, if an `Array` of values is passed in only those values will be disabled. + + + +### readOnly?{ type: '[Boolean, Array]' } + +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} dropdown list. Providing a `string` will group +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 +This component is used to render each option group, when `groupBy` is specified. By default the `groupBy` value will be used. @@ -80,7 +92,7 @@ are always "startsWith", meaning it will search from the start of the `textField ### filter?{ type: '[Boolean, String, Function(dataItem, searchTerm)]', default: 'false' } Specify a filtering method used to reduce the items in the dropdown as you type. It can be used in conjunction with -the `suggest` prop or instead of it. There are a few prebuilt filtering methods that can be specified +the `suggest` prop or instead of it. 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` diff --git a/docs/components/pages/DropdownList.api.md b/docs/components/pages/DropdownList.api.md index befc68973..bccb76031 100644 --- a/docs/components/pages/DropdownList.api.md +++ b/docs/components/pages/DropdownList.api.md @@ -12,59 +12,71 @@ Change event Handler that is called when the value is changed. ### 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. ### data?{ type: 'Array' } -provide an array of possible values for the DropdownList. If an array of `objects` is provided you +provide an array of possible values for the ${widgetName}. If an array of `objects` is provided you should use the `valueField` and `textField` props, to specify which object properties comprise the value field (such as an id) and the field used to label the item. ### 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`} ### valueComponent?{ type: 'Component' } -This component is used to render the selected value of the combobox. The default component +This component is used to render the selected value of the ${widgetName}. The default component renders the text of the selected item (specified by `textfield`) ### itemComponent?{ type: 'Component' } -This component is used to render each possible item in the DropdownList. The default component +This component is used to render each possible item in the ${widgetName}. The default component renders the text of the selected item (specified by `textfield`) +### disabled?{ type: '[Boolean, Array]' } + +Disable the widget, if an `Array` of values is passed in only those values will be disabled. + + + +### readOnly?{ type: '[Boolean, Array]' } + +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} dropdown list. Providing a `string` will group +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 +This component is used to render each option group, when `groupBy` is specified. By default the `groupBy` value will be used. @@ -79,7 +91,7 @@ Text to display when the value is empty. The string value of the current search being typed into the {widgetName}. When unset (`undefined`) the {widgetName} will handle the filtering internally. -The `defaultSearchTerm` prop can be used to set an initialization value for uncontrolled widgets. searchTerm is only +The `defaultSearchTerm` prop can be used to set an initialization value for uncontrolled widgets. `searchTerm` is only relevant when the `filter` prop is set. @@ -105,7 +117,7 @@ when the `open` prop is set otherwise the widget open buttons won't work. ### filter?{ type: '[String, Function(dataItem, searchTerm)]', default: 'false' } -Specify a filtering method used to reduce the items in the dropdown as you type. There are a few prebuilt filtering +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. To handle custom filtering techniques provide a `function` that returns `true` or `false` for each passed in item @@ -168,4 +180,4 @@ Text to display when the the current filter does not return any results. - home move focus to first item - end move focus to last item - enter select focused item -- any key search list for item starting with key \ No newline at end of file +- any key search list for item starting with key diff --git a/src/Combobox.jsx b/src/Combobox.jsx index 8eeb6fa5e..8a8f36c3f 100644 --- a/src/Combobox.jsx +++ b/src/Combobox.jsx @@ -12,7 +12,7 @@ import GroupableList from './ListGroupable'; import validateList from './util/validateListInterface'; import createUncontrolledWidget from 'uncontrollable'; import { dataItem, dataText, dataIndexOf } from './util/dataHelpers'; -import { widgetEditable, widgetEnabled } from './util/interaction'; +import { widgetEditable, widgetEnabled, isDisabled, isReadOnly } from './util/interaction'; import { instanceId, notify, isFirstFocusedRender } from './util/widgetHelpers'; let defaultSuggest = f => f === true ? 'startsWith' : f ? f : 'eq' @@ -41,8 +41,8 @@ let propTypes = { onSelect: React.PropTypes.func, autoFocus: React.PropTypes.bool, - disabled: CustomPropTypes.disabled, - readOnly: CustomPropTypes.readOnly, + disabled: CustomPropTypes.disabled.acceptsArray, + readOnly: CustomPropTypes.readOnly.acceptsArray, suggest: CustomPropTypes.filter, filter: CustomPropTypes.filter, @@ -144,7 +144,7 @@ var ComboBox = React.createClass({ className, tabIndex, filter, suggest , valueField, textField, groupBy , messages, data, busy, dropUp, name, autoFocus - , placeholder, value, open, disabled, readOnly + , placeholder, value, open , listComponent: List } = this.props; List = List || (groupBy && GroupableList) || PlainList @@ -156,6 +156,8 @@ var ComboBox = React.createClass({ let { focusedItem, selectedItem, focused } = this.state; let items = this._data() + , disabled = isDisabled(this.props) + , readOnly = isReadOnly(this.props) , valueItem = dataItem(data, value, valueField) // take value from the raw data , inputID = instanceId(this, '_input') , listID = instanceId(this, '_listbox') diff --git a/src/DropdownList.jsx b/src/DropdownList.jsx index 6fe3dfb07..5d290011c 100644 --- a/src/DropdownList.jsx +++ b/src/DropdownList.jsx @@ -12,7 +12,7 @@ import validateList from './util/validateListInterface'; import createUncontrolledWidget from 'uncontrollable'; import { dataItem, dataText, dataIndexOf } from './util/dataHelpers'; -import { widgetEditable, widgetEnabled } from './util/interaction'; +import { widgetEditable, widgetEnabled, isDisabled, isReadOnly } from './util/interaction'; import { instanceId, notify, isFirstFocusedRender } from './util/widgetHelpers'; let { omit, pick, result } = _; @@ -48,9 +48,8 @@ var propTypes = { dropUp: React.PropTypes.bool, duration: React.PropTypes.number, //popup - disabled: CustomPropTypes.disabled, - - readOnly: CustomPropTypes.readOnly, + disabled: CustomPropTypes.disabled.acceptsArray, + readOnly: CustomPropTypes.readOnly.acceptsArray, messages: React.PropTypes.shape({ open: CustomPropTypes.message, @@ -123,7 +122,7 @@ var DropdownList = React.createClass({ className, tabIndex, filter , valueField, textField, groupBy , messages, data, busy, dropUp - , placeholder, value, open, disabled, readOnly + , placeholder, value, open , valueComponent: ValueComponent , listComponent: List } = this.props; @@ -136,6 +135,8 @@ var DropdownList = React.createClass({ let { focusedItem, selectedItem, focused } = this.state; let items = this._data() + , disabled = isDisabled(this.props) + , readOnly = isReadOnly(this.props) , valueItem = dataItem(data, value, valueField) // take value from the raw data , listID = instanceId(this, '__listbox'); diff --git a/src/List.jsx b/src/List.jsx index 7c94835b2..e36c1b214 100644 --- a/src/List.jsx +++ b/src/List.jsx @@ -6,6 +6,7 @@ import cn from 'classnames'; import _ from './util/_'; import { dataText, dataValue } from './util/dataHelpers'; import { instanceId, notify } from './util/widgetHelpers'; +import { isDisabledItem, isReadOnlyItem } from './util/interaction'; let optionId = (id, idx)=> `${id}__option__${idx}`; @@ -26,12 +27,13 @@ export default React.createClass({ optionComponent: CustomPropTypes.elementType, itemComponent: CustomPropTypes.elementType, - selectedIndex: React.PropTypes.number, - focusedIndex: React.PropTypes.number, - valueField: React.PropTypes.string, + selected: React.PropTypes.any, + focused: React.PropTypes.any, + valueField: CustomPropTypes.accessor, textField: CustomPropTypes.accessor, - optionID: React.PropTypes.func, + disabled: CustomPropTypes.disabled.acceptsArray, + readOnly: CustomPropTypes.readOnly.acceptsArray, messages: React.PropTypes.shape({ emptyList: CustomPropTypes.message @@ -41,7 +43,6 @@ export default React.createClass({ getDefaultProps(){ return { - optID: '', onSelect: ()=>{}, optionComponent: ListOption, ariaActiveDescendantKey: 'list', @@ -72,7 +73,6 @@ export default React.createClass({ , focused, selected, messages, onSelect , itemComponent: ItemComponent , optionComponent: Option - , optionID , ...props } = this.props , id = instanceId(this) , items; @@ -83,22 +83,28 @@ export default React.createClass({ {_.result(messages.emptyList, this.props)} ) : data.map((item, idx) => { - var currentId = optionId(id, idx); + var currentId = optionId(id, idx) + , isDisabled = isDisabledItem(item, props) + , isReadOnly = isReadOnlyItem(item, props); return (