UNPKG

10.8 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4
5var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
6
7Object.defineProperty(exports, "__esModule", {
8 value: true
9});
10exports.default = void 0;
11
12var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
13
14var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
15
16var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
17
18var React = _interopRequireWildcard(require("react"));
19
20var _KeyCode = _interopRequireDefault(require("rc-util/lib/KeyCode"));
21
22var _pickAttrs = _interopRequireDefault(require("rc-util/lib/pickAttrs"));
23
24var _useMemo = _interopRequireDefault(require("rc-util/lib/hooks/useMemo"));
25
26var _classnames = _interopRequireDefault(require("classnames"));
27
28var _rcVirtualList = _interopRequireDefault(require("rc-virtual-list"));
29
30var _TransBtn = _interopRequireDefault(require("./TransBtn"));
31
32/**
33 * Using virtual list of option display.
34 * Will fallback to dom if use customize render.
35 */
36var OptionList = function OptionList(_ref, ref) {
37 var prefixCls = _ref.prefixCls,
38 id = _ref.id,
39 flattenOptions = _ref.flattenOptions,
40 childrenAsData = _ref.childrenAsData,
41 values = _ref.values,
42 searchValue = _ref.searchValue,
43 multiple = _ref.multiple,
44 defaultActiveFirstOption = _ref.defaultActiveFirstOption,
45 height = _ref.height,
46 itemHeight = _ref.itemHeight,
47 notFoundContent = _ref.notFoundContent,
48 open = _ref.open,
49 menuItemSelectedIcon = _ref.menuItemSelectedIcon,
50 virtual = _ref.virtual,
51 onSelect = _ref.onSelect,
52 onToggleOpen = _ref.onToggleOpen,
53 onActiveValue = _ref.onActiveValue,
54 onScroll = _ref.onScroll,
55 onMouseEnter = _ref.onMouseEnter;
56 var itemPrefixCls = "".concat(prefixCls, "-item");
57 var memoFlattenOptions = (0, _useMemo.default)(function () {
58 return flattenOptions;
59 }, [open, flattenOptions], function (prev, next) {
60 return next[0] && prev[1] !== next[1];
61 }); // =========================== List ===========================
62
63 var listRef = React.useRef(null);
64
65 var onListMouseDown = function onListMouseDown(event) {
66 event.preventDefault();
67 };
68
69 var scrollIntoView = function scrollIntoView(index) {
70 if (listRef.current) {
71 listRef.current.scrollTo({
72 index: index
73 });
74 }
75 }; // ========================== Active ==========================
76
77
78 var getEnabledActiveIndex = function getEnabledActiveIndex(index) {
79 var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
80 var len = memoFlattenOptions.length;
81
82 for (var i = 0; i < len; i += 1) {
83 var current = (index + i * offset + len) % len;
84 var _memoFlattenOptions$c = memoFlattenOptions[current],
85 group = _memoFlattenOptions$c.group,
86 data = _memoFlattenOptions$c.data;
87
88 if (!group && !data.disabled) {
89 return current;
90 }
91 }
92
93 return -1;
94 };
95
96 var _React$useState = React.useState(function () {
97 return getEnabledActiveIndex(0);
98 }),
99 _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
100 activeIndex = _React$useState2[0],
101 setActiveIndex = _React$useState2[1];
102
103 var setActive = function setActive(index) {
104 setActiveIndex(index); // Trigger active event
105
106 var flattenItem = memoFlattenOptions[index];
107
108 if (!flattenItem) {
109 onActiveValue(null, -1);
110 return;
111 }
112
113 onActiveValue(flattenItem.data.value, index);
114 }; // Auto active first item when list length or searchValue changed
115
116
117 React.useEffect(function () {
118 setActive(defaultActiveFirstOption !== false ? getEnabledActiveIndex(0) : -1);
119 }, [memoFlattenOptions.length, searchValue]); // Auto scroll to item position in single mode
120
121 React.useEffect(function () {
122 /**
123 * React will skip `onChange` when component update.
124 * `setActive` function will call root accessibility state update which makes re-render.
125 * So we need to delay to let Input component trigger onChange first.
126 */
127 var timeoutId = setTimeout(function () {
128 if (!multiple && open && values.size === 1) {
129 var value = Array.from(values)[0];
130 var index = memoFlattenOptions.findIndex(function (_ref2) {
131 var data = _ref2.data;
132 return data.value === value;
133 });
134 setActive(index);
135 scrollIntoView(index);
136 }
137 });
138 return function () {
139 return clearTimeout(timeoutId);
140 };
141 }, [open]); // ========================== Values ==========================
142
143 var onSelectValue = function onSelectValue(value) {
144 if (value !== undefined) {
145 onSelect(value, {
146 selected: !values.has(value)
147 });
148 } // Single mode should always close by select
149
150
151 if (!multiple) {
152 onToggleOpen(false);
153 }
154 }; // ========================= Keyboard =========================
155
156
157 React.useImperativeHandle(ref, function () {
158 return {
159 onKeyDown: function onKeyDown(event) {
160 var which = event.which;
161
162 switch (which) {
163 // >>> Arrow keys
164 case _KeyCode.default.UP:
165 case _KeyCode.default.DOWN:
166 {
167 var offset = 0;
168
169 if (which === _KeyCode.default.UP) {
170 offset = -1;
171 } else if (which === _KeyCode.default.DOWN) {
172 offset = 1;
173 }
174
175 if (offset !== 0) {
176 var nextActiveIndex = getEnabledActiveIndex(activeIndex + offset, offset);
177 scrollIntoView(nextActiveIndex);
178 setActive(nextActiveIndex);
179 }
180
181 break;
182 }
183 // >>> Select
184
185 case _KeyCode.default.ENTER:
186 {
187 // value
188 var item = memoFlattenOptions[activeIndex];
189
190 if (item && !item.data.disabled) {
191 onSelectValue(item.data.value);
192 } else {
193 onSelectValue(undefined);
194 }
195
196 if (open) {
197 event.preventDefault();
198 }
199
200 break;
201 }
202 // >>> Close
203
204 case _KeyCode.default.ESC:
205 {
206 onToggleOpen(false);
207 }
208 }
209 },
210 onKeyUp: function onKeyUp() {},
211 scrollTo: function scrollTo(index) {
212 scrollIntoView(index);
213 }
214 };
215 }); // ========================== Render ==========================
216
217 if (memoFlattenOptions.length === 0) {
218 return React.createElement("div", {
219 role: "listbox",
220 id: "".concat(id, "_list"),
221 className: "".concat(itemPrefixCls, "-empty"),
222 onMouseDown: onListMouseDown
223 }, notFoundContent);
224 }
225
226 function renderItem(index) {
227 var item = memoFlattenOptions[index];
228 if (!item) return null;
229 var itemData = item.data || {};
230 var value = itemData.value,
231 label = itemData.label,
232 children = itemData.children;
233 var attrs = (0, _pickAttrs.default)(itemData, true);
234 var mergedLabel = childrenAsData ? children : label;
235 return item ? React.createElement("div", Object.assign({
236 "aria-label": typeof mergedLabel === 'string' ? mergedLabel : null
237 }, attrs, {
238 key: index,
239 role: "option",
240 id: "".concat(id, "_list_").concat(index),
241 "aria-selected": values.has(value)
242 }), value) : null;
243 }
244
245 return React.createElement(React.Fragment, null, React.createElement("div", {
246 role: "listbox",
247 id: "".concat(id, "_list"),
248 style: {
249 height: 0,
250 width: 0,
251 overflow: 'hidden'
252 }
253 }, renderItem(activeIndex - 1), renderItem(activeIndex), renderItem(activeIndex + 1)), React.createElement(_rcVirtualList.default, {
254 itemKey: "key",
255 ref: listRef,
256 data: memoFlattenOptions,
257 height: height,
258 itemHeight: itemHeight,
259 fullHeight: false,
260 onMouseDown: onListMouseDown,
261 onScroll: onScroll,
262 virtual: virtual,
263 onMouseEnter: onMouseEnter
264 }, function (_ref3, itemIndex) {
265 var _classNames;
266
267 var group = _ref3.group,
268 groupOption = _ref3.groupOption,
269 data = _ref3.data;
270 var label = data.label,
271 key = data.key; // Group
272
273 if (group) {
274 return React.createElement("div", {
275 className: (0, _classnames.default)(itemPrefixCls, "".concat(itemPrefixCls, "-group"))
276 }, label !== undefined ? label : key);
277 }
278
279 var disabled = data.disabled,
280 value = data.value,
281 title = data.title,
282 children = data.children,
283 style = data.style,
284 className = data.className,
285 otherProps = (0, _objectWithoutProperties2.default)(data, ["disabled", "value", "title", "children", "style", "className"]); // Option
286
287 var selected = values.has(value);
288 var optionPrefixCls = "".concat(itemPrefixCls, "-option");
289 var optionClassName = (0, _classnames.default)(itemPrefixCls, optionPrefixCls, className, (_classNames = {}, (0, _defineProperty2.default)(_classNames, "".concat(optionPrefixCls, "-grouped"), groupOption), (0, _defineProperty2.default)(_classNames, "".concat(optionPrefixCls, "-active"), activeIndex === itemIndex && !disabled), (0, _defineProperty2.default)(_classNames, "".concat(optionPrefixCls, "-disabled"), disabled), (0, _defineProperty2.default)(_classNames, "".concat(optionPrefixCls, "-selected"), selected), _classNames));
290 var mergedLabel = childrenAsData ? children : label;
291 var iconVisible = !menuItemSelectedIcon || typeof menuItemSelectedIcon === 'function' || selected;
292 return React.createElement("div", Object.assign({}, otherProps, {
293 "aria-selected": selected,
294 className: optionClassName,
295 title: title,
296 onMouseMove: function onMouseMove() {
297 if (activeIndex === itemIndex || disabled) {
298 return;
299 }
300
301 setActive(itemIndex);
302 },
303 onClick: function onClick() {
304 if (!disabled) {
305 onSelectValue(value);
306 }
307 },
308 style: style
309 }), React.createElement("div", {
310 className: "".concat(optionPrefixCls, "-content")
311 }, mergedLabel || value), React.isValidElement(menuItemSelectedIcon) || selected, iconVisible && React.createElement(_TransBtn.default, {
312 className: "".concat(itemPrefixCls, "-option-state"),
313 customizeIcon: menuItemSelectedIcon,
314 customizeIconProps: {
315 isSelected: selected
316 }
317 }, selected ? '✓' : null));
318 }));
319};
320
321var RefOptionList = React.forwardRef(OptionList);
322RefOptionList.displayName = 'OptionList';
323var _default = RefOptionList;
324exports.default = _default;
\No newline at end of file