1 | const _excluded = ["defaultValue", "value", "onChange", "textField", "dataKey", "data", "onKeyDown", "disabled", "readOnly", "onBlur", "onFocus", "multiple"];
|
2 |
|
3 | function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
4 |
|
5 | function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
6 |
|
7 |
|
8 | import PropTypes from 'prop-types';
|
9 | import cn from 'classnames';
|
10 | import React, { useMemo, useRef } from 'react';
|
11 | import { useUncontrolledProp } from 'uncontrollable';
|
12 | import List, { useHandleSelect } from './List';
|
13 | import { useFocusList, FocusListContext } from './FocusListContext';
|
14 | import * as CustomPropTypes from './PropTypes';
|
15 | import { makeArray } from './_';
|
16 | import { useAccessors } from './Accessors';
|
17 | import { notify } from './WidgetHelpers';
|
18 | import useFocusManager from './useFocusManager';
|
19 | import { useWidgetProps } from './Widget';
|
20 | const propTypes = {
|
21 | data: PropTypes.array,
|
22 | dataKey: CustomPropTypes.accessor,
|
23 | textField: CustomPropTypes.accessor,
|
24 | onSelect: PropTypes.func,
|
25 | onMove: PropTypes.func,
|
26 | onHoverOption: PropTypes.func,
|
27 | optionComponent: PropTypes.elementType,
|
28 | renderItem: PropTypes.func,
|
29 | renderGroup: PropTypes.func,
|
30 | focusedItem: PropTypes.any,
|
31 | selectedItem: PropTypes.any,
|
32 | searchTerm: PropTypes.string,
|
33 |
|
34 | |
35 |
|
36 |
|
37 | disabled: CustomPropTypes.disabled.acceptsArray,
|
38 | messages: PropTypes.shape({
|
39 | emptyList: PropTypes.func.isRequired
|
40 | })
|
41 | };
|
42 | const Listbox = React.forwardRef(function Listbox(_ref, _outerRef) {
|
43 | let {
|
44 | defaultValue,
|
45 | value: propsValue,
|
46 | onChange: propsOnChange,
|
47 | textField,
|
48 | dataKey,
|
49 | data,
|
50 | onKeyDown,
|
51 | disabled,
|
52 | readOnly,
|
53 | onBlur,
|
54 | onFocus,
|
55 | multiple
|
56 | } = _ref,
|
57 | props = _objectWithoutPropertiesLoose(_ref, _excluded);
|
58 |
|
59 | const [value, onChange] = useUncontrolledProp(propsValue, defaultValue, propsOnChange);
|
60 | const accessors = useAccessors(textField, dataKey);
|
61 | const dataItems = useMemo(() => makeArray(value, multiple).map(item => accessors.findOrSelf(data, item)), [value, multiple, accessors, data]);
|
62 | const ref = useRef(null);
|
63 | const lastItemRef = useRef(dataItems[dataItems.length - 1]);
|
64 | const list = useFocusList({
|
65 | scope: ref,
|
66 | anchorItem: lastItemRef.current
|
67 | });
|
68 | const isDisabled = disabled === true;
|
69 |
|
70 | const handleChange = (dataItem, meta) => {
|
71 | if (isDisabled || readOnly) return;
|
72 | lastItemRef.current = meta.dataItem;
|
73 | onChange(dataItem, meta);
|
74 | };
|
75 |
|
76 | const handleSelect = useHandleSelect(!!multiple, dataItems, handleChange);
|
77 | const [focusEvents, focused] = useFocusManager(ref, {
|
78 | disabled: isDisabled,
|
79 | onBlur,
|
80 | onFocus
|
81 | }, {
|
82 | didHandle(focused) {
|
83 | if (!focused) {
|
84 | list.focus(undefined);
|
85 | } else {
|
86 | focus({
|
87 | preventScroll: true
|
88 | });
|
89 | }
|
90 | }
|
91 |
|
92 | });
|
93 |
|
94 | function focus(opts) {
|
95 | if (ref.current) ref.current.focus(opts);
|
96 | }
|
97 |
|
98 | const handleKeyDown = e => {
|
99 | if (isDisabled || readOnly) return;
|
100 | let {
|
101 | key,
|
102 | shiftKey
|
103 | } = e;
|
104 | notify(onKeyDown, [e]);
|
105 | if (e.defaultPrevented) return;
|
106 |
|
107 | if (key === 'End' && !shiftKey) {
|
108 | e.preventDefault();
|
109 | list.focus(list.last());
|
110 | } else if (key === 'Home' && !shiftKey) {
|
111 | e.preventDefault();
|
112 | list.focus(list.first());
|
113 | } else if (key === 'Enter' || key === ' ') {
|
114 | e.preventDefault();
|
115 | if (list.getFocused()) handleSelect(list.getFocused(), e);
|
116 | } else if (key === 'ArrowDown') {
|
117 | e.preventDefault();
|
118 | list.focus(list.next());
|
119 | } else if (key === 'ArrowUp') {
|
120 | e.preventDefault();
|
121 | list.focus(list.prev());
|
122 | }
|
123 | };
|
124 |
|
125 | const widgetProps = useWidgetProps({
|
126 | focused,
|
127 | readOnly,
|
128 | disabled: isDisabled,
|
129 | className: cn(props.className, 'rw-listbox rw-widget-input rw-widget')
|
130 | });
|
131 | return React.createElement(FocusListContext.Provider, {
|
132 | value: list.context
|
133 | }, React.createElement(List, _extends({}, props, widgetProps, {
|
134 | disabled: disabled,
|
135 | tabIndex: isDisabled ? -1 : 0,
|
136 | data: data,
|
137 | elementRef: ref,
|
138 | value: dataItems,
|
139 | multiple: multiple,
|
140 | accessors: accessors
|
141 | }, focusEvents, {
|
142 | onChange: handleChange,
|
143 | onKeyDown: handleKeyDown
|
144 | })));
|
145 | });
|
146 | Listbox.displayName = 'Listbox';
|
147 | Listbox.propTypes = propTypes;
|
148 | export default Listbox; |
\ | No newline at end of file |