@rc-component/cascader 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/LICENSE.md +9 -0
  2. package/README.md +300 -0
  3. package/assets/index.less +3 -0
  4. package/assets/list.less +106 -0
  5. package/assets/panel.less +7 -0
  6. package/assets/select.less +3 -0
  7. package/es/Cascader.d.ts +88 -0
  8. package/es/Cascader.js +230 -0
  9. package/es/OptionList/CacheContent.d.ts +7 -0
  10. package/es/OptionList/CacheContent.js +8 -0
  11. package/es/OptionList/Checkbox.d.ts +10 -0
  12. package/es/OptionList/Checkbox.js +24 -0
  13. package/es/OptionList/Column.d.ts +21 -0
  14. package/es/OptionList/Column.js +175 -0
  15. package/es/OptionList/List.d.ts +6 -0
  16. package/es/OptionList/List.js +216 -0
  17. package/es/OptionList/index.d.ts +4 -0
  18. package/es/OptionList/index.js +13 -0
  19. package/es/OptionList/useActive.d.ts +6 -0
  20. package/es/OptionList/useActive.js +26 -0
  21. package/es/OptionList/useKeyboard.d.ts +10 -0
  22. package/es/OptionList/useKeyboard.js +165 -0
  23. package/es/Panel.d.ts +5 -0
  24. package/es/Panel.js +116 -0
  25. package/es/context.d.ts +21 -0
  26. package/es/context.js +3 -0
  27. package/es/hooks/useDisplayValues.d.ts +10 -0
  28. package/es/hooks/useDisplayValues.js +44 -0
  29. package/es/hooks/useEntities.d.ts +10 -0
  30. package/es/hooks/useEntities.js +35 -0
  31. package/es/hooks/useMissingValues.d.ts +3 -0
  32. package/es/hooks/useMissingValues.js +17 -0
  33. package/es/hooks/useOptions.d.ts +9 -0
  34. package/es/hooks/useOptions.js +20 -0
  35. package/es/hooks/useSearchConfig.d.ts +2 -0
  36. package/es/hooks/useSearchConfig.js +27 -0
  37. package/es/hooks/useSearchOptions.d.ts +4 -0
  38. package/es/hooks/useSearchOptions.js +63 -0
  39. package/es/hooks/useSelect.d.ts +4 -0
  40. package/es/hooks/useSelect.js +49 -0
  41. package/es/hooks/useValues.d.ts +9 -0
  42. package/es/hooks/useValues.js +21 -0
  43. package/es/index.d.ts +5 -0
  44. package/es/index.js +4 -0
  45. package/es/utils/commonUtil.d.ts +18 -0
  46. package/es/utils/commonUtil.js +69 -0
  47. package/es/utils/treeUtil.d.ts +9 -0
  48. package/es/utils/treeUtil.js +35 -0
  49. package/es/utils/warningPropsUtil.d.ts +4 -0
  50. package/es/utils/warningPropsUtil.js +29 -0
  51. package/lib/Cascader.d.ts +88 -0
  52. package/lib/Cascader.js +239 -0
  53. package/lib/OptionList/CacheContent.d.ts +7 -0
  54. package/lib/OptionList/CacheContent.js +16 -0
  55. package/lib/OptionList/Checkbox.d.ts +10 -0
  56. package/lib/OptionList/Checkbox.js +33 -0
  57. package/lib/OptionList/Column.d.ts +21 -0
  58. package/lib/OptionList/Column.js +185 -0
  59. package/lib/OptionList/List.d.ts +6 -0
  60. package/lib/OptionList/List.js +224 -0
  61. package/lib/OptionList/index.d.ts +4 -0
  62. package/lib/OptionList/index.js +22 -0
  63. package/lib/OptionList/useActive.d.ts +6 -0
  64. package/lib/OptionList/useActive.js +34 -0
  65. package/lib/OptionList/useKeyboard.d.ts +10 -0
  66. package/lib/OptionList/useKeyboard.js +175 -0
  67. package/lib/Panel.d.ts +5 -0
  68. package/lib/Panel.js +125 -0
  69. package/lib/context.d.ts +21 -0
  70. package/lib/context.js +11 -0
  71. package/lib/hooks/useDisplayValues.d.ts +10 -0
  72. package/lib/hooks/useDisplayValues.js +53 -0
  73. package/lib/hooks/useEntities.d.ts +10 -0
  74. package/lib/hooks/useEntities.js +44 -0
  75. package/lib/hooks/useMissingValues.d.ts +3 -0
  76. package/lib/hooks/useMissingValues.js +25 -0
  77. package/lib/hooks/useOptions.d.ts +9 -0
  78. package/lib/hooks/useOptions.js +29 -0
  79. package/lib/hooks/useSearchConfig.d.ts +2 -0
  80. package/lib/hooks/useSearchConfig.js +36 -0
  81. package/lib/hooks/useSearchOptions.d.ts +4 -0
  82. package/lib/hooks/useSearchOptions.js +71 -0
  83. package/lib/hooks/useSelect.d.ts +4 -0
  84. package/lib/hooks/useSelect.js +55 -0
  85. package/lib/hooks/useValues.d.ts +9 -0
  86. package/lib/hooks/useValues.js +29 -0
  87. package/lib/index.d.ts +5 -0
  88. package/lib/index.js +16 -0
  89. package/lib/utils/commonUtil.d.ts +18 -0
  90. package/lib/utils/commonUtil.js +83 -0
  91. package/lib/utils/treeUtil.d.ts +9 -0
  92. package/lib/utils/treeUtil.js +42 -0
  93. package/lib/utils/warningPropsUtil.d.ts +4 -0
  94. package/lib/utils/warningPropsUtil.js +37 -0
  95. package/package.json +88 -0
@@ -0,0 +1,216 @@
1
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : 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); }
2
+ /* eslint-disable default-case */
3
+ import classNames from 'classnames';
4
+ import * as React from 'react';
5
+ import CascaderContext from "../context";
6
+ import { getFullPathKeys, isLeaf, scrollIntoParentView, toPathKey, toPathKeys, toPathValueStr } from "../utils/commonUtil";
7
+ import { toPathOptions } from "../utils/treeUtil";
8
+ import CacheContent from "./CacheContent";
9
+ import Column, { FIX_LABEL } from "./Column";
10
+ import useActive from "./useActive";
11
+ import useKeyboard from "./useKeyboard";
12
+ const RawOptionList = /*#__PURE__*/React.forwardRef((props, ref) => {
13
+ const {
14
+ prefixCls,
15
+ multiple,
16
+ searchValue,
17
+ toggleOpen,
18
+ notFoundContent,
19
+ direction,
20
+ open,
21
+ disabled
22
+ } = props;
23
+ const containerRef = React.useRef(null);
24
+ const rtl = direction === 'rtl';
25
+ const {
26
+ options,
27
+ values,
28
+ halfValues,
29
+ fieldNames,
30
+ changeOnSelect,
31
+ onSelect,
32
+ searchOptions,
33
+ dropdownPrefixCls,
34
+ loadData,
35
+ expandTrigger
36
+ } = React.useContext(CascaderContext);
37
+ const mergedPrefixCls = dropdownPrefixCls || prefixCls;
38
+
39
+ // ========================= loadData =========================
40
+ const [loadingKeys, setLoadingKeys] = React.useState([]);
41
+ const internalLoadData = valueCells => {
42
+ // Do not load when search
43
+ if (!loadData || searchValue) {
44
+ return;
45
+ }
46
+ const optionList = toPathOptions(valueCells, options, fieldNames);
47
+ const rawOptions = optionList.map(({
48
+ option
49
+ }) => option);
50
+ const lastOption = rawOptions[rawOptions.length - 1];
51
+ if (lastOption && !isLeaf(lastOption, fieldNames)) {
52
+ const pathKey = toPathKey(valueCells);
53
+ setLoadingKeys(keys => [...keys, pathKey]);
54
+ loadData(rawOptions);
55
+ }
56
+ };
57
+
58
+ // zombieJ: This is bad. We should make this same as `rc-tree` to use Promise instead.
59
+ React.useEffect(() => {
60
+ if (loadingKeys.length) {
61
+ loadingKeys.forEach(loadingKey => {
62
+ const valueStrCells = toPathValueStr(loadingKey);
63
+ const optionList = toPathOptions(valueStrCells, options, fieldNames, true).map(({
64
+ option
65
+ }) => option);
66
+ const lastOption = optionList[optionList.length - 1];
67
+ if (!lastOption || lastOption[fieldNames.children] || isLeaf(lastOption, fieldNames)) {
68
+ setLoadingKeys(keys => keys.filter(key => key !== loadingKey));
69
+ }
70
+ });
71
+ }
72
+ }, [options, loadingKeys, fieldNames]);
73
+
74
+ // ========================== Values ==========================
75
+ const checkedSet = React.useMemo(() => new Set(toPathKeys(values)), [values]);
76
+ const halfCheckedSet = React.useMemo(() => new Set(toPathKeys(halfValues)), [halfValues]);
77
+
78
+ // ====================== Accessibility =======================
79
+ const [activeValueCells, setActiveValueCells] = useActive(multiple, open);
80
+
81
+ // =========================== Path ===========================
82
+ const onPathOpen = nextValueCells => {
83
+ setActiveValueCells(nextValueCells);
84
+
85
+ // Trigger loadData
86
+ internalLoadData(nextValueCells);
87
+ };
88
+ const isSelectable = option => {
89
+ if (disabled) {
90
+ return false;
91
+ }
92
+ const {
93
+ disabled: optionDisabled
94
+ } = option;
95
+ const isMergedLeaf = isLeaf(option, fieldNames);
96
+ return !optionDisabled && (isMergedLeaf || changeOnSelect || multiple);
97
+ };
98
+ const onPathSelect = (valuePath, leaf, fromKeyboard = false) => {
99
+ onSelect(valuePath);
100
+ if (!multiple && (leaf || changeOnSelect && (expandTrigger === 'hover' || fromKeyboard))) {
101
+ toggleOpen(false);
102
+ }
103
+ };
104
+
105
+ // ========================== Option ==========================
106
+ const mergedOptions = React.useMemo(() => {
107
+ if (searchValue) {
108
+ return searchOptions;
109
+ }
110
+ return options;
111
+ }, [searchValue, searchOptions, options]);
112
+
113
+ // ========================== Column ==========================
114
+ const optionColumns = React.useMemo(() => {
115
+ const optionList = [{
116
+ options: mergedOptions
117
+ }];
118
+ let currentList = mergedOptions;
119
+ const fullPathKeys = getFullPathKeys(currentList, fieldNames);
120
+ for (let i = 0; i < activeValueCells.length; i += 1) {
121
+ const activeValueCell = activeValueCells[i];
122
+ const currentOption = currentList.find((option, index) => (fullPathKeys[index] ? toPathKey(fullPathKeys[index]) : option[fieldNames.value]) === activeValueCell);
123
+ const subOptions = currentOption?.[fieldNames.children];
124
+ if (!subOptions?.length) {
125
+ break;
126
+ }
127
+ currentList = subOptions;
128
+ optionList.push({
129
+ options: subOptions
130
+ });
131
+ }
132
+ return optionList;
133
+ }, [mergedOptions, activeValueCells, fieldNames]);
134
+
135
+ // ========================= Keyboard =========================
136
+ const onKeyboardSelect = (selectValueCells, option) => {
137
+ if (isSelectable(option)) {
138
+ onPathSelect(selectValueCells, isLeaf(option, fieldNames), true);
139
+ }
140
+ };
141
+ useKeyboard(ref, mergedOptions, fieldNames, activeValueCells, onPathOpen, onKeyboardSelect, {
142
+ direction,
143
+ searchValue,
144
+ toggleOpen,
145
+ open
146
+ });
147
+
148
+ // >>>>> Active Scroll
149
+ React.useEffect(() => {
150
+ if (searchValue) {
151
+ return;
152
+ }
153
+ for (let i = 0; i < activeValueCells.length; i += 1) {
154
+ const cellPath = activeValueCells.slice(0, i + 1);
155
+ const cellKeyPath = toPathKey(cellPath);
156
+ const ele = containerRef.current?.querySelector(`li[data-path-key="${cellKeyPath.replace(/\\{0,2}"/g, '\\"')}"]` // matches unescaped double quotes
157
+ );
158
+
159
+ if (ele) {
160
+ scrollIntoParentView(ele);
161
+ }
162
+ }
163
+ }, [activeValueCells, searchValue]);
164
+
165
+ // ========================== Render ==========================
166
+ // >>>>> Empty
167
+ const isEmpty = !optionColumns[0]?.options?.length;
168
+ const emptyList = [{
169
+ [fieldNames.value]: '__EMPTY__',
170
+ [FIX_LABEL]: notFoundContent,
171
+ disabled: true
172
+ }];
173
+ const columnProps = {
174
+ ...props,
175
+ multiple: !isEmpty && multiple,
176
+ onSelect: onPathSelect,
177
+ onActive: onPathOpen,
178
+ onToggleOpen: toggleOpen,
179
+ checkedSet,
180
+ halfCheckedSet,
181
+ loadingKeys,
182
+ isSelectable
183
+ };
184
+
185
+ // >>>>> Columns
186
+ const mergedOptionColumns = isEmpty ? [{
187
+ options: emptyList
188
+ }] : optionColumns;
189
+ const columnNodes = mergedOptionColumns.map((col, index) => {
190
+ const prevValuePath = activeValueCells.slice(0, index);
191
+ const activeValue = activeValueCells[index];
192
+ return /*#__PURE__*/React.createElement(Column, _extends({
193
+ key: index
194
+ }, columnProps, {
195
+ prefixCls: mergedPrefixCls,
196
+ options: col.options,
197
+ prevValuePath: prevValuePath,
198
+ activeValue: activeValue
199
+ }));
200
+ });
201
+
202
+ // >>>>> Render
203
+ return /*#__PURE__*/React.createElement(CacheContent, {
204
+ open: open
205
+ }, /*#__PURE__*/React.createElement("div", {
206
+ className: classNames(`${mergedPrefixCls}-menus`, {
207
+ [`${mergedPrefixCls}-menu-empty`]: isEmpty,
208
+ [`${mergedPrefixCls}-rtl`]: rtl
209
+ }),
210
+ ref: containerRef
211
+ }, columnNodes));
212
+ });
213
+ if (process.env.NODE_ENV !== 'production') {
214
+ RawOptionList.displayName = 'RawOptionList';
215
+ }
216
+ export default RawOptionList;
@@ -0,0 +1,4 @@
1
+ import type { RefOptionListProps } from '@rc-component/select/lib/OptionList';
2
+ import * as React from 'react';
3
+ declare const RefOptionList: React.ForwardRefExoticComponent<React.RefAttributes<RefOptionListProps>>;
4
+ export default RefOptionList;
@@ -0,0 +1,13 @@
1
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : 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); }
2
+ import { useBaseProps } from '@rc-component/select';
3
+ import * as React from 'react';
4
+ import RawOptionList from "./List";
5
+ const RefOptionList = /*#__PURE__*/React.forwardRef((props, ref) => {
6
+ const baseProps = useBaseProps();
7
+
8
+ // >>>>> Render
9
+ return /*#__PURE__*/React.createElement(RawOptionList, _extends({}, props, baseProps, {
10
+ ref: ref
11
+ }));
12
+ });
13
+ export default RefOptionList;
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+ /**
3
+ * Control the active open options path.
4
+ */
5
+ declare const useActive: (multiple?: boolean, open?: boolean) => [React.Key[], (activeValueCells: React.Key[]) => void];
6
+ export default useActive;
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ import CascaderContext from "../context";
3
+
4
+ /**
5
+ * Control the active open options path.
6
+ */
7
+ const useActive = (multiple, open) => {
8
+ const {
9
+ values
10
+ } = React.useContext(CascaderContext);
11
+ const firstValueCells = values[0];
12
+
13
+ // Record current dropdown active options
14
+ // This also control the open status
15
+ const [activeValueCells, setActiveValueCells] = React.useState([]);
16
+ React.useEffect(() => {
17
+ if (!multiple) {
18
+ setActiveValueCells(firstValueCells || []);
19
+ }
20
+ }, /* eslint-disable react-hooks/exhaustive-deps */
21
+ [open, firstValueCells]
22
+ /* eslint-enable react-hooks/exhaustive-deps */);
23
+
24
+ return [activeValueCells, setActiveValueCells];
25
+ };
26
+ export default useActive;
@@ -0,0 +1,10 @@
1
+ import type { RefOptionListProps } from '@rc-component/select/lib/OptionList';
2
+ import * as React from 'react';
3
+ import type { DefaultOptionType, InternalFieldNames, SingleValueType } from '../Cascader';
4
+ declare const _default: (ref: React.Ref<RefOptionListProps>, options: DefaultOptionType[], fieldNames: InternalFieldNames, activeValueCells: React.Key[], setActiveValueCells: (activeValueCells: React.Key[]) => void, onKeyBoardSelect: (valueCells: SingleValueType, option: DefaultOptionType) => void, contextProps: {
5
+ direction?: "ltr" | "rtl" | undefined;
6
+ searchValue: string;
7
+ toggleOpen: (open?: boolean) => void;
8
+ open?: boolean | undefined;
9
+ }) => void;
10
+ export default _default;
@@ -0,0 +1,165 @@
1
+ import KeyCode from "@rc-component/util/es/KeyCode";
2
+ import * as React from 'react';
3
+ import { SEARCH_MARK } from "../hooks/useSearchOptions";
4
+ import { getFullPathKeys, toPathKey } from "../utils/commonUtil";
5
+ export default ((ref, options, fieldNames, activeValueCells, setActiveValueCells, onKeyBoardSelect, contextProps) => {
6
+ const {
7
+ direction,
8
+ searchValue,
9
+ toggleOpen,
10
+ open
11
+ } = contextProps;
12
+ const rtl = direction === 'rtl';
13
+ const [validActiveValueCells, lastActiveIndex, lastActiveOptions, fullPathKeys] = React.useMemo(() => {
14
+ let activeIndex = -1;
15
+ let currentOptions = options;
16
+ const mergedActiveIndexes = [];
17
+ const mergedActiveValueCells = [];
18
+ const len = activeValueCells.length;
19
+ const pathKeys = getFullPathKeys(options, fieldNames);
20
+
21
+ // Fill validate active value cells and index
22
+ for (let i = 0; i < len && currentOptions; i += 1) {
23
+ // Mark the active index for current options
24
+ const nextActiveIndex = currentOptions.findIndex((option, index) => (pathKeys[index] ? toPathKey(pathKeys[index]) : option[fieldNames.value]) === activeValueCells[i]);
25
+ if (nextActiveIndex === -1) {
26
+ break;
27
+ }
28
+ activeIndex = nextActiveIndex;
29
+ mergedActiveIndexes.push(activeIndex);
30
+ mergedActiveValueCells.push(activeValueCells[i]);
31
+ currentOptions = currentOptions[activeIndex][fieldNames.children];
32
+ }
33
+
34
+ // Fill last active options
35
+ let activeOptions = options;
36
+ for (let i = 0; i < mergedActiveIndexes.length - 1; i += 1) {
37
+ activeOptions = activeOptions[mergedActiveIndexes[i]][fieldNames.children];
38
+ }
39
+ return [mergedActiveValueCells, activeIndex, activeOptions, pathKeys];
40
+ }, [activeValueCells, fieldNames, options]);
41
+
42
+ // Update active value cells and scroll to target element
43
+ const internalSetActiveValueCells = next => {
44
+ setActiveValueCells(next);
45
+ };
46
+
47
+ // Same options offset
48
+ const offsetActiveOption = offset => {
49
+ const len = lastActiveOptions.length;
50
+ let currentIndex = lastActiveIndex;
51
+ if (currentIndex === -1 && offset < 0) {
52
+ currentIndex = len;
53
+ }
54
+ for (let i = 0; i < len; i += 1) {
55
+ currentIndex = (currentIndex + offset + len) % len;
56
+ const option = lastActiveOptions[currentIndex];
57
+ if (option && !option.disabled) {
58
+ const nextActiveCells = validActiveValueCells.slice(0, -1).concat(fullPathKeys[currentIndex] ? toPathKey(fullPathKeys[currentIndex]) : option[fieldNames.value]);
59
+ internalSetActiveValueCells(nextActiveCells);
60
+ return;
61
+ }
62
+ }
63
+ };
64
+
65
+ // Different options offset
66
+ const prevColumn = () => {
67
+ if (validActiveValueCells.length > 1) {
68
+ const nextActiveCells = validActiveValueCells.slice(0, -1);
69
+ internalSetActiveValueCells(nextActiveCells);
70
+ } else {
71
+ toggleOpen(false);
72
+ }
73
+ };
74
+ const nextColumn = () => {
75
+ const nextOptions = lastActiveOptions[lastActiveIndex]?.[fieldNames.children] || [];
76
+ const nextOption = nextOptions.find(option => !option.disabled);
77
+ if (nextOption) {
78
+ const nextActiveCells = [...validActiveValueCells, nextOption[fieldNames.value]];
79
+ internalSetActiveValueCells(nextActiveCells);
80
+ }
81
+ };
82
+ React.useImperativeHandle(ref, () => ({
83
+ // scrollTo: treeRef.current?.scrollTo,
84
+ onKeyDown: event => {
85
+ const {
86
+ which
87
+ } = event;
88
+ switch (which) {
89
+ // >>> Arrow keys
90
+ case KeyCode.UP:
91
+ case KeyCode.DOWN:
92
+ {
93
+ let offset = 0;
94
+ if (which === KeyCode.UP) {
95
+ offset = -1;
96
+ } else if (which === KeyCode.DOWN) {
97
+ offset = 1;
98
+ }
99
+ if (offset !== 0) {
100
+ offsetActiveOption(offset);
101
+ }
102
+ break;
103
+ }
104
+ case KeyCode.LEFT:
105
+ {
106
+ if (searchValue) {
107
+ break;
108
+ }
109
+ if (rtl) {
110
+ nextColumn();
111
+ } else {
112
+ prevColumn();
113
+ }
114
+ break;
115
+ }
116
+ case KeyCode.RIGHT:
117
+ {
118
+ if (searchValue) {
119
+ break;
120
+ }
121
+ if (rtl) {
122
+ prevColumn();
123
+ } else {
124
+ nextColumn();
125
+ }
126
+ break;
127
+ }
128
+ case KeyCode.BACKSPACE:
129
+ {
130
+ if (!searchValue) {
131
+ prevColumn();
132
+ }
133
+ break;
134
+ }
135
+
136
+ // >>> Select
137
+ case KeyCode.ENTER:
138
+ {
139
+ if (validActiveValueCells.length) {
140
+ const option = lastActiveOptions[lastActiveIndex];
141
+
142
+ // Search option should revert back of origin options
143
+ const originOptions = option?.[SEARCH_MARK] || [];
144
+ if (originOptions.length) {
145
+ onKeyBoardSelect(originOptions.map(opt => opt[fieldNames.value]), originOptions[originOptions.length - 1]);
146
+ } else {
147
+ onKeyBoardSelect(validActiveValueCells, lastActiveOptions[lastActiveIndex]);
148
+ }
149
+ }
150
+ break;
151
+ }
152
+
153
+ // >>> Close
154
+ case KeyCode.ESC:
155
+ {
156
+ toggleOpen(false);
157
+ if (open) {
158
+ event.stopPropagation();
159
+ }
160
+ }
161
+ }
162
+ },
163
+ onKeyUp: () => {}
164
+ }));
165
+ });
package/es/Panel.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import * as React from 'react';
2
+ import type { CascaderProps, DefaultOptionType } from './Cascader';
3
+ export type PickType = 'value' | 'defaultValue' | 'changeOnSelect' | 'onChange' | 'options' | 'prefixCls' | 'checkable' | 'fieldNames' | 'showCheckedStrategy' | 'loadData' | 'expandTrigger' | 'expandIcon' | 'loadingIcon' | 'className' | 'style' | 'direction' | 'notFoundContent' | 'disabled';
4
+ export type PanelProps<OptionType extends DefaultOptionType = DefaultOptionType, ValueField extends keyof OptionType = keyof OptionType, Multiple extends boolean | React.ReactNode = false> = Pick<CascaderProps<OptionType, ValueField, Multiple>, PickType>;
5
+ export default function Panel<OptionType extends DefaultOptionType = DefaultOptionType, ValueField extends keyof OptionType = keyof OptionType, Multiple extends boolean | React.ReactNode = false>(props: PanelProps<OptionType, ValueField, Multiple>): React.JSX.Element;
package/es/Panel.js ADDED
@@ -0,0 +1,116 @@
1
+ import classNames from 'classnames';
2
+ import { useEvent, useMergedState } from '@rc-component/util';
3
+ import * as React from 'react';
4
+ import CascaderContext from "./context";
5
+ import useMissingValues from "./hooks/useMissingValues";
6
+ import useOptions from "./hooks/useOptions";
7
+ import useSelect from "./hooks/useSelect";
8
+ import useValues from "./hooks/useValues";
9
+ import RawOptionList from "./OptionList/List";
10
+ import { fillFieldNames, toRawValues } from "./utils/commonUtil";
11
+ import { toPathOptions } from "./utils/treeUtil";
12
+ function noop() {}
13
+ export default function Panel(props) {
14
+ const {
15
+ prefixCls = 'rc-cascader',
16
+ style,
17
+ className,
18
+ options,
19
+ checkable,
20
+ defaultValue,
21
+ value,
22
+ fieldNames,
23
+ changeOnSelect,
24
+ onChange,
25
+ showCheckedStrategy,
26
+ loadData,
27
+ expandTrigger,
28
+ expandIcon = '>',
29
+ loadingIcon,
30
+ direction,
31
+ notFoundContent = 'Not Found',
32
+ disabled
33
+ } = props;
34
+
35
+ // ======================== Multiple ========================
36
+ const multiple = !!checkable;
37
+
38
+ // ========================= Values =========================
39
+ const [rawValues, setRawValues] = useMergedState(defaultValue, {
40
+ value,
41
+ postState: toRawValues
42
+ });
43
+
44
+ // ========================= FieldNames =========================
45
+ const mergedFieldNames = React.useMemo(() => fillFieldNames(fieldNames), /* eslint-disable react-hooks/exhaustive-deps */
46
+ [JSON.stringify(fieldNames)]
47
+ /* eslint-enable react-hooks/exhaustive-deps */);
48
+
49
+ // =========================== Option ===========================
50
+ const [mergedOptions, getPathKeyEntities, getValueByKeyPath] = useOptions(mergedFieldNames, options);
51
+
52
+ // ========================= Values =========================
53
+ const getMissingValues = useMissingValues(mergedOptions, mergedFieldNames);
54
+
55
+ // Fill `rawValues` with checked conduction values
56
+ const [checkedValues, halfCheckedValues, missingCheckedValues] = useValues(multiple, rawValues, getPathKeyEntities, getValueByKeyPath, getMissingValues);
57
+
58
+ // =========================== Change ===========================
59
+ const triggerChange = useEvent(nextValues => {
60
+ setRawValues(nextValues);
61
+
62
+ // Save perf if no need trigger event
63
+ if (onChange) {
64
+ const nextRawValues = toRawValues(nextValues);
65
+ const valueOptions = nextRawValues.map(valueCells => toPathOptions(valueCells, mergedOptions, mergedFieldNames).map(valueOpt => valueOpt.option));
66
+ const triggerValues = multiple ? nextRawValues : nextRawValues[0];
67
+ const triggerOptions = multiple ? valueOptions : valueOptions[0];
68
+ onChange(triggerValues, triggerOptions);
69
+ }
70
+ });
71
+
72
+ // =========================== Select ===========================
73
+ const handleSelection = useSelect(multiple, triggerChange, checkedValues, halfCheckedValues, missingCheckedValues, getPathKeyEntities, getValueByKeyPath, showCheckedStrategy);
74
+ const onInternalSelect = useEvent(valuePath => {
75
+ handleSelection(valuePath);
76
+ });
77
+
78
+ // ======================== Context =========================
79
+ const cascaderContext = React.useMemo(() => ({
80
+ options: mergedOptions,
81
+ fieldNames: mergedFieldNames,
82
+ values: checkedValues,
83
+ halfValues: halfCheckedValues,
84
+ changeOnSelect,
85
+ onSelect: onInternalSelect,
86
+ checkable,
87
+ searchOptions: [],
88
+ dropdownPrefixCls: undefined,
89
+ loadData,
90
+ expandTrigger,
91
+ expandIcon,
92
+ loadingIcon,
93
+ dropdownMenuColumnStyle: undefined
94
+ }), [mergedOptions, mergedFieldNames, checkedValues, halfCheckedValues, changeOnSelect, onInternalSelect, checkable, loadData, expandTrigger, expandIcon, loadingIcon]);
95
+
96
+ // ========================= Render =========================
97
+ const panelPrefixCls = `${prefixCls}-panel`;
98
+ const isEmpty = !mergedOptions.length;
99
+ return /*#__PURE__*/React.createElement(CascaderContext.Provider, {
100
+ value: cascaderContext
101
+ }, /*#__PURE__*/React.createElement("div", {
102
+ className: classNames(panelPrefixCls, {
103
+ [`${panelPrefixCls}-rtl`]: direction === 'rtl',
104
+ [`${panelPrefixCls}-empty`]: isEmpty
105
+ }, className),
106
+ style: style
107
+ }, isEmpty ? notFoundContent : /*#__PURE__*/React.createElement(RawOptionList, {
108
+ prefixCls: prefixCls,
109
+ searchValue: "",
110
+ multiple: multiple,
111
+ toggleOpen: noop,
112
+ open: true,
113
+ direction: direction,
114
+ disabled: disabled
115
+ })));
116
+ }
@@ -0,0 +1,21 @@
1
+ import * as React from 'react';
2
+ import type { CascaderProps, InternalFieldNames, DefaultOptionType, SingleValueType } from './Cascader';
3
+ export interface CascaderContextProps {
4
+ options: NonNullable<CascaderProps['options']>;
5
+ fieldNames: InternalFieldNames;
6
+ values: SingleValueType[];
7
+ halfValues: SingleValueType[];
8
+ changeOnSelect?: boolean;
9
+ onSelect: (valuePath: SingleValueType) => void;
10
+ checkable?: boolean | React.ReactNode;
11
+ searchOptions: DefaultOptionType[];
12
+ dropdownPrefixCls?: string;
13
+ loadData?: (selectOptions: DefaultOptionType[]) => void;
14
+ expandTrigger?: 'hover' | 'click';
15
+ expandIcon?: React.ReactNode;
16
+ loadingIcon?: React.ReactNode;
17
+ dropdownMenuColumnStyle?: React.CSSProperties;
18
+ optionRender?: CascaderProps['optionRender'];
19
+ }
20
+ declare const CascaderContext: React.Context<CascaderContextProps>;
21
+ export default CascaderContext;
package/es/context.js ADDED
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+ const CascaderContext = /*#__PURE__*/React.createContext({});
3
+ export default CascaderContext;
@@ -0,0 +1,10 @@
1
+ import * as React from 'react';
2
+ import type { DefaultOptionType, SingleValueType, CascaderProps, InternalFieldNames } from '../Cascader';
3
+ declare const _default: (rawValues: SingleValueType[], options: DefaultOptionType[], fieldNames: InternalFieldNames, multiple: boolean, displayRender: CascaderProps['displayRender']) => {
4
+ label: React.ReactNode;
5
+ value: string;
6
+ key: string;
7
+ valueCells: SingleValueType;
8
+ disabled: boolean | undefined;
9
+ }[];
10
+ export default _default;
@@ -0,0 +1,44 @@
1
+ import { toPathOptions } from "../utils/treeUtil";
2
+ import * as React from 'react';
3
+ import { toPathKey } from "../utils/commonUtil";
4
+ export default ((rawValues, options, fieldNames, multiple, displayRender) => {
5
+ return React.useMemo(() => {
6
+ const mergedDisplayRender = displayRender || (
7
+ // Default displayRender
8
+ labels => {
9
+ const mergedLabels = multiple ? labels.slice(-1) : labels;
10
+ const SPLIT = ' / ';
11
+ if (mergedLabels.every(label => ['string', 'number'].includes(typeof label))) {
12
+ return mergedLabels.join(SPLIT);
13
+ }
14
+
15
+ // If exist non-string value, use ReactNode instead
16
+ return mergedLabels.reduce((list, label, index) => {
17
+ const keyedLabel = /*#__PURE__*/React.isValidElement(label) ? /*#__PURE__*/React.cloneElement(label, {
18
+ key: index
19
+ }) : label;
20
+ if (index === 0) {
21
+ return [keyedLabel];
22
+ }
23
+ return [...list, SPLIT, keyedLabel];
24
+ }, []);
25
+ });
26
+ return rawValues.map(valueCells => {
27
+ const valueOptions = toPathOptions(valueCells, options, fieldNames);
28
+ const label = mergedDisplayRender(valueOptions.map(({
29
+ option,
30
+ value
31
+ }) => option?.[fieldNames.label] ?? value), valueOptions.map(({
32
+ option
33
+ }) => option));
34
+ const value = toPathKey(valueCells);
35
+ return {
36
+ label,
37
+ value,
38
+ key: value,
39
+ valueCells,
40
+ disabled: valueOptions[valueOptions.length - 1]?.option?.disabled
41
+ };
42
+ });
43
+ }, [rawValues, options, fieldNames, displayRender, multiple]);
44
+ });
@@ -0,0 +1,10 @@
1
+ import type { DefaultOptionType, InternalFieldNames } from '../Cascader';
2
+ import type { DataEntity } from 'rc-tree/lib/interface';
3
+ export interface OptionsInfo {
4
+ keyEntities: Record<string, DataEntity>;
5
+ pathKeyEntities: Record<string, DataEntity>;
6
+ }
7
+ export type GetEntities = () => OptionsInfo['pathKeyEntities'];
8
+ /** Lazy parse options data into conduct-able info to avoid perf issue in single mode */
9
+ declare const _default: (options: DefaultOptionType[], fieldNames: InternalFieldNames) => GetEntities;
10
+ export default _default;