@rainbow-oh/yee-c 0.6.4 → 0.8.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 (145) hide show
  1. package/dist/cjs/Button/style/index.less +5 -4
  2. package/dist/cjs/Drawer/drawer.js +1 -1
  3. package/dist/cjs/Drawer/interface.d.ts +1 -1
  4. package/dist/cjs/Drawer/style/index.less +1 -0
  5. package/dist/cjs/Form/form-store.d.ts +2 -1
  6. package/dist/cjs/Form/form-store.js +32 -17
  7. package/dist/cjs/List/demo/virtual.d.ts +3 -0
  8. package/dist/cjs/List/demo/virtual.js +53 -0
  9. package/dist/cjs/List/hooks/useListKeyboard.d.ts +28 -0
  10. package/dist/cjs/List/hooks/useListKeyboard.js +188 -0
  11. package/dist/cjs/List/interface.d.ts +29 -2
  12. package/dist/cjs/List/list.js +141 -21
  13. package/dist/cjs/List/style/index.less +24 -0
  14. package/dist/cjs/Select/demo/columns.d.ts +3 -0
  15. package/dist/cjs/Select/demo/columns.js +62 -0
  16. package/dist/cjs/Select/hooks/useSelectKeyboard.d.ts +13 -1
  17. package/dist/cjs/Select/hooks/useSelectKeyboard.js +81 -10
  18. package/dist/cjs/Select/interface.d.ts +30 -3
  19. package/dist/cjs/Select/option.js +4 -3
  20. package/dist/cjs/Select/options.js +90 -8
  21. package/dist/cjs/Select/select.js +32 -8
  22. package/dist/cjs/Select/style/index.less +14 -0
  23. package/dist/cjs/Selector/interface.d.ts +4 -0
  24. package/dist/cjs/Selector/selector.js +5 -3
  25. package/dist/cjs/Table/hooks/useColumns.js +1 -1
  26. package/dist/cjs/Table/interface.d.ts +1 -1
  27. package/dist/cjs/Table/style/index.less +35 -11
  28. package/dist/cjs/Table/table.js +57 -7
  29. package/dist/cjs/Table/tbody/cell.js +1 -1
  30. package/dist/cjs/Table/util/header.d.ts +3 -1
  31. package/dist/cjs/Table/util/header.js +30 -3
  32. package/dist/cjs/Tree/demo/draggable.js +2 -0
  33. package/dist/cjs/Tree/interface.d.ts +46 -23
  34. package/dist/cjs/Tree/tree-node.d.ts +1 -1
  35. package/dist/cjs/Tree/tree-node.js +30 -23
  36. package/dist/cjs/Tree/tree.d.ts +13 -12
  37. package/dist/cjs/Tree/tree.js +452 -227
  38. package/dist/cjs/Tree/utils/tree-tools.d.ts +43 -10
  39. package/dist/cjs/Tree/utils/tree-tools.js +129 -91
  40. package/dist/cjs/locale/context.d.ts +5 -4
  41. package/dist/cjs/locale/context.js +15 -8
  42. package/dist/cjs/locale/interface.d.ts +20 -0
  43. package/dist/cjs/locale/util.d.ts +3 -3
  44. package/dist/cjs/style/color.css +3 -0
  45. package/dist/cjs/style/size.css +2 -1
  46. package/dist/cjs/style/themes.css +1 -1
  47. package/dist/cjs/style/variables.css +6 -2
  48. package/dist/cjs/utils/isEqual.js +5 -7
  49. package/dist/cjs/version.d.ts +1 -1
  50. package/dist/cjs/version.js +1 -1
  51. package/dist/esm/Button/style/index.less +5 -4
  52. package/dist/esm/Drawer/drawer.js +2 -1
  53. package/dist/esm/Drawer/interface.d.ts +1 -1
  54. package/dist/esm/Drawer/style/index.less +1 -0
  55. package/dist/esm/Form/form-store.d.ts +2 -1
  56. package/dist/esm/Form/form-store.js +40 -19
  57. package/dist/esm/List/demo/virtual.d.ts +3 -0
  58. package/dist/esm/List/demo/virtual.js +22 -0
  59. package/dist/esm/List/hooks/useListKeyboard.d.ts +28 -0
  60. package/dist/esm/List/hooks/useListKeyboard.js +173 -0
  61. package/dist/esm/List/interface.d.ts +29 -2
  62. package/dist/esm/List/list.js +171 -14
  63. package/dist/esm/List/style/index.less +24 -0
  64. package/dist/esm/Select/demo/columns.d.ts +3 -0
  65. package/dist/esm/Select/demo/columns.js +36 -0
  66. package/dist/esm/Select/hooks/useSelectKeyboard.d.ts +13 -1
  67. package/dist/esm/Select/hooks/useSelectKeyboard.js +91 -14
  68. package/dist/esm/Select/interface.d.ts +30 -3
  69. package/dist/esm/Select/option.js +6 -2
  70. package/dist/esm/Select/options.js +114 -32
  71. package/dist/esm/Select/select.js +40 -11
  72. package/dist/esm/Select/style/index.less +14 -0
  73. package/dist/esm/Selector/interface.d.ts +4 -0
  74. package/dist/esm/Selector/selector.js +10 -4
  75. package/dist/esm/Table/hooks/useColumns.js +1 -1
  76. package/dist/esm/Table/interface.d.ts +1 -1
  77. package/dist/esm/Table/style/index.less +35 -11
  78. package/dist/esm/Table/table.js +54 -2
  79. package/dist/esm/Table/tbody/cell.js +1 -1
  80. package/dist/esm/Table/util/header.d.ts +3 -1
  81. package/dist/esm/Table/util/header.js +30 -2
  82. package/dist/esm/Tree/demo/draggable.js +2 -0
  83. package/dist/esm/Tree/interface.d.ts +46 -23
  84. package/dist/esm/Tree/tree-node.d.ts +1 -1
  85. package/dist/esm/Tree/tree-node.js +26 -31
  86. package/dist/esm/Tree/tree.d.ts +13 -12
  87. package/dist/esm/Tree/tree.js +572 -163
  88. package/dist/esm/Tree/utils/tree-tools.d.ts +43 -10
  89. package/dist/esm/Tree/utils/tree-tools.js +219 -104
  90. package/dist/esm/locale/context.d.ts +5 -4
  91. package/dist/esm/locale/context.js +25 -9
  92. package/dist/esm/locale/interface.d.ts +20 -0
  93. package/dist/esm/locale/util.d.ts +3 -3
  94. package/dist/esm/style/color.css +3 -0
  95. package/dist/esm/style/size.css +2 -1
  96. package/dist/esm/style/themes.css +1 -1
  97. package/dist/esm/style/variables.css +6 -2
  98. package/dist/esm/utils/isEqual.js +5 -7
  99. package/dist/esm/version.d.ts +1 -1
  100. package/dist/esm/version.js +1 -1
  101. package/dist/umd/yee-c.min.css +4 -4
  102. package/dist/umd/yee-c.min.js +1 -1
  103. package/package.json +1 -1
  104. package/src/Button/style/index.less +5 -4
  105. package/src/Drawer/drawer.tsx +1 -1
  106. package/src/Drawer/interface.ts +1 -0
  107. package/src/Drawer/style/index.less +1 -0
  108. package/src/Form/form-store.ts +42 -15
  109. package/src/List/demo/virtual.tsx +20 -0
  110. package/src/List/hooks/useListKeyboard.ts +235 -0
  111. package/src/List/index.en-US.md +6 -0
  112. package/src/List/index.zh-CN.md +6 -0
  113. package/src/List/interface.ts +29 -2
  114. package/src/List/list.tsx +157 -19
  115. package/src/List/style/index.less +24 -0
  116. package/src/Select/demo/columns.tsx +35 -0
  117. package/src/Select/hooks/useSelectKeyboard.ts +112 -15
  118. package/src/Select/index.en-US.md +4 -0
  119. package/src/Select/index.zh-CN.md +4 -0
  120. package/src/Select/interface.ts +31 -4
  121. package/src/Select/option.tsx +9 -3
  122. package/src/Select/options.tsx +119 -26
  123. package/src/Select/select.tsx +41 -8
  124. package/src/Select/style/index.less +14 -0
  125. package/src/Selector/interface.ts +4 -0
  126. package/src/Selector/selector.tsx +8 -3
  127. package/src/Table/hooks/useColumns.ts +1 -1
  128. package/src/Table/interface.ts +1 -1
  129. package/src/Table/style/index.less +35 -11
  130. package/src/Table/table.tsx +60 -1
  131. package/src/Table/tbody/cell.tsx +1 -1
  132. package/src/Table/util/header.ts +48 -2
  133. package/src/Tree/demo/draggable.tsx +2 -0
  134. package/src/Tree/interface.ts +46 -23
  135. package/src/Tree/tree-node.tsx +26 -29
  136. package/src/Tree/tree.tsx +573 -274
  137. package/src/Tree/utils/tree-tools.ts +231 -125
  138. package/src/locale/_README.md +22 -13
  139. package/src/locale/context.tsx +33 -16
  140. package/src/locale/interface.ts +24 -0
  141. package/src/locale/util.ts +6 -6
  142. package/src/style/color.css +3 -0
  143. package/src/style/size.css +2 -1
  144. package/src/utils/isEqual.ts +5 -7
  145. package/src/version.ts +1 -1
@@ -6,18 +6,42 @@
6
6
  list-style: none;
7
7
  background-color: var(--yee-list-bg-color);
8
8
 
9
+ &:focus {
10
+ outline: none;
11
+ }
12
+
9
13
  &-item {
10
14
  padding: var(--yee-list-item-padding);
11
15
 
12
16
  &:hover:not(&-disabled) {
13
17
  background-color: var(--yee-list-item-bg-hover-color);
18
+ }
19
+
20
+ &-disabled {
14
21
  cursor: not-allowed;
22
+ opacity: 0.5;
23
+ }
24
+
25
+ &-clickable:not(&-disabled) {
26
+ cursor: pointer;
15
27
  }
16
28
 
17
29
  &-focused {
18
30
  background-color: var(--yee-list-item-bg-hover-color);
19
31
  }
20
32
  }
33
+
34
+ // Multi-column grid layout (non-virtual mode).
35
+ // Column count driven by CSS custom property --yee-list-columns set inline.
36
+ &-grid {
37
+ display: grid;
38
+ grid-template-columns: repeat(var(--yee-list-columns, 1), 1fr);
39
+ }
40
+
41
+ // Virtual multi-column: each row is already a grid via inline style.
42
+ &-grid-row {
43
+ align-items: center;
44
+ }
21
45
  }
22
46
 
23
47
  @{pls}-bordered {
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ declare const _default: () => React.JSX.Element;
3
+ export default _default;
@@ -0,0 +1,36 @@
1
+ import { Select } from "../..";
2
+ import React from 'react';
3
+ var options = Array.from({
4
+ length: 24
5
+ }, function (_, i) {
6
+ return {
7
+ label: "Option ".concat(i + 1),
8
+ value: "opt-".concat(i + 1)
9
+ };
10
+ });
11
+ export default (function () {
12
+ return /*#__PURE__*/React.createElement("div", {
13
+ style: {
14
+ display: 'flex',
15
+ flexDirection: 'column',
16
+ gap: 24
17
+ }
18
+ }, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("p", null, "3 columns:"), /*#__PURE__*/React.createElement(Select, {
19
+ columns: 3,
20
+ popupWidth: 360,
21
+ options: options,
22
+ placeholder: "Select an option",
23
+ style: {
24
+ width: 200
25
+ }
26
+ })), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("p", null, "4 columns with multiple selection:"), /*#__PURE__*/React.createElement(Select, {
27
+ columns: 4,
28
+ popupWidth: 480,
29
+ mode: "multiple",
30
+ options: options,
31
+ placeholder: "Select options",
32
+ style: {
33
+ width: 300
34
+ }
35
+ })));
36
+ });
@@ -7,6 +7,18 @@ interface UseSelectKeyboardParams {
7
7
  }>;
8
8
  open: boolean;
9
9
  selectedKeys: Array<string | number>;
10
+ /**
11
+ * Number of columns in multi-column mode.
12
+ * Affects ArrowLeft/ArrowRight and ArrowUp/ArrowDown behavior.
13
+ * @default 1
14
+ */
15
+ columns?: number;
16
+ /**
17
+ * Whether loose value matching is enabled.
18
+ * Affects initial focus resolution when selectedKeys types differ from option values.
19
+ * @default false
20
+ */
21
+ looseMatch?: boolean;
10
22
  onSelect: (value: string | number) => void;
11
23
  onClose?: () => void;
12
24
  onOpenChange?: (open: boolean) => void;
@@ -18,7 +30,7 @@ interface UseSelectKeyboardParams {
18
30
  */
19
31
  scrollToIndex?: (index: number) => void;
20
32
  }
21
- export default function useSelectKeyboard({ options, open, selectedKeys, onSelect, onClose, onOpenChange, containerRef, scrollToIndex, }: UseSelectKeyboardParams): {
33
+ export default function useSelectKeyboard({ options, open, selectedKeys, columns, looseMatch, onSelect, onClose, onOpenChange, containerRef, scrollToIndex, }: UseSelectKeyboardParams): {
22
34
  focusedKey: string | number;
23
35
  onKeyDown: (event: React.KeyboardEvent) => void;
24
36
  };
@@ -10,6 +10,10 @@ export default function useSelectKeyboard(_ref) {
10
10
  var options = _ref.options,
11
11
  open = _ref.open,
12
12
  selectedKeys = _ref.selectedKeys,
13
+ _ref$columns = _ref.columns,
14
+ columns = _ref$columns === void 0 ? 1 : _ref$columns,
15
+ _ref$looseMatch = _ref.looseMatch,
16
+ looseMatch = _ref$looseMatch === void 0 ? false : _ref$looseMatch,
13
17
  onSelect = _ref.onSelect,
14
18
  onClose = _ref.onClose,
15
19
  onOpenChange = _ref.onOpenChange,
@@ -63,9 +67,15 @@ export default function useSelectKeyboard(_ref) {
63
67
  var optionElements = container.querySelectorAll('[role="option"]');
64
68
  var targetElement = optionElements[index];
65
69
  if (targetElement) {
66
- targetElement.scrollIntoView({
67
- block: 'nearest'
68
- });
70
+ // Use manual scroll calculation instead of scrollIntoView to avoid
71
+ // scrolling the entire page (popup is rendered in a portal).
72
+ var containerRect = container.getBoundingClientRect();
73
+ var targetRect = targetElement.getBoundingClientRect();
74
+ if (targetRect.bottom > containerRect.bottom) {
75
+ container.scrollTop += targetRect.bottom - containerRect.bottom;
76
+ } else if (targetRect.top < containerRect.top) {
77
+ container.scrollTop -= containerRect.top - targetRect.top;
78
+ }
69
79
  }
70
80
  };
71
81
 
@@ -73,12 +83,29 @@ export default function useSelectKeyboard(_ref) {
73
83
  var handleArrowUp = function handleArrowUp() {
74
84
  var focusedIndex = getFocusedIndex();
75
85
  var nextIndex;
76
- if (focusedIndex === -1) {
77
- // No focus yet, focus the last enabled option
78
- nextIndex = findNextEnabledIndex(options.length - 1, -1);
86
+ if (columns > 1) {
87
+ // Multi-column: move up one row (jump by `columns` items)
88
+ if (focusedIndex === -1) {
89
+ nextIndex = findNextEnabledIndex(options.length - 1, -1);
90
+ } else {
91
+ var target = focusedIndex - columns;
92
+ if (target >= 0) {
93
+ nextIndex = options[target].disabled ? findNextEnabledIndex(target, -1) : target;
94
+ } else {
95
+ var _options$wrapTarget;
96
+ // Wrap to bottom: find the same column in the last row
97
+ var lastRowStart = Math.floor((options.length - 1) / columns) * columns;
98
+ var col = focusedIndex % columns;
99
+ var wrapTarget = Math.min(lastRowStart + col, options.length - 1);
100
+ nextIndex = (_options$wrapTarget = options[wrapTarget]) !== null && _options$wrapTarget !== void 0 && _options$wrapTarget.disabled ? findNextEnabledIndex(wrapTarget, -1) : wrapTarget;
101
+ }
102
+ }
79
103
  } else {
80
- // Find the previous enabled option
81
- nextIndex = findNextEnabledIndex(focusedIndex, -1);
104
+ if (focusedIndex === -1) {
105
+ nextIndex = findNextEnabledIndex(options.length - 1, -1);
106
+ } else {
107
+ nextIndex = findNextEnabledIndex(focusedIndex, -1);
108
+ }
82
109
  }
83
110
  if (nextIndex !== -1) {
84
111
  var nextValue = options[nextIndex].value;
@@ -91,12 +118,28 @@ export default function useSelectKeyboard(_ref) {
91
118
  var handleArrowDown = function handleArrowDown() {
92
119
  var focusedIndex = getFocusedIndex();
93
120
  var nextIndex;
94
- if (focusedIndex === -1) {
95
- // No focus yet, focus the first enabled option
96
- nextIndex = findNextEnabledIndex(0, 1);
121
+ if (columns > 1) {
122
+ // Multi-column: move down one row (jump by `columns` items)
123
+ if (focusedIndex === -1) {
124
+ nextIndex = findNextEnabledIndex(0, 1);
125
+ } else {
126
+ var target = focusedIndex + columns;
127
+ if (target < options.length) {
128
+ nextIndex = options[target].disabled ? findNextEnabledIndex(target, 1) : target;
129
+ } else {
130
+ var _options$wrapTarget2;
131
+ // Wrap to top: find the same column in the first row
132
+ var col = focusedIndex % columns;
133
+ var wrapTarget = col;
134
+ nextIndex = (_options$wrapTarget2 = options[wrapTarget]) !== null && _options$wrapTarget2 !== void 0 && _options$wrapTarget2.disabled ? findNextEnabledIndex(wrapTarget, 1) : wrapTarget;
135
+ }
136
+ }
97
137
  } else {
98
- // Find the next enabled option
99
- nextIndex = findNextEnabledIndex(focusedIndex, 1);
138
+ if (focusedIndex === -1) {
139
+ nextIndex = findNextEnabledIndex(0, 1);
140
+ } else {
141
+ nextIndex = findNextEnabledIndex(focusedIndex, 1);
142
+ }
100
143
  }
101
144
  if (nextIndex !== -1) {
102
145
  var nextValue = options[nextIndex].value;
@@ -105,6 +148,28 @@ export default function useSelectKeyboard(_ref) {
105
148
  }
106
149
  };
107
150
 
151
+ // Handle ArrowLeft key (multi-column: move to previous item)
152
+ var handleArrowLeft = function handleArrowLeft() {
153
+ var focusedIndex = getFocusedIndex();
154
+ if (focusedIndex <= 0) return;
155
+ var nextIndex = findNextEnabledIndex(focusedIndex, -1);
156
+ if (nextIndex !== -1) {
157
+ setFocusedKey(options[nextIndex].value);
158
+ scrollOptionIntoView(nextIndex);
159
+ }
160
+ };
161
+
162
+ // Handle ArrowRight key (multi-column: move to next item)
163
+ var handleArrowRight = function handleArrowRight() {
164
+ var focusedIndex = getFocusedIndex();
165
+ if (focusedIndex >= options.length - 1) return;
166
+ var nextIndex = findNextEnabledIndex(focusedIndex, 1);
167
+ if (nextIndex !== -1) {
168
+ setFocusedKey(options[nextIndex].value);
169
+ scrollOptionIntoView(nextIndex);
170
+ }
171
+ };
172
+
108
173
  // Handle Enter key
109
174
  var handleEnter = function handleEnter() {
110
175
  var currentFocusedKey = latestFocusedKey.current;
@@ -143,6 +208,18 @@ export default function useSelectKeyboard(_ref) {
143
208
  event.preventDefault();
144
209
  handleArrowDown();
145
210
  break;
211
+ case 'ArrowLeft':
212
+ if (columns > 1) {
213
+ event.preventDefault();
214
+ handleArrowLeft();
215
+ }
216
+ break;
217
+ case 'ArrowRight':
218
+ if (columns > 1) {
219
+ event.preventDefault();
220
+ handleArrowRight();
221
+ }
222
+ break;
146
223
  case 'Enter':
147
224
  event.preventDefault();
148
225
  handleEnter();
@@ -162,7 +239,7 @@ export default function useSelectKeyboard(_ref) {
162
239
  var focusedValue = '';
163
240
  if ((selectedKeys === null || selectedKeys === void 0 ? void 0 : selectedKeys.length) > 0) {
164
241
  var firstSelected = options === null || options === void 0 ? void 0 : options.find(function (opt) {
165
- return opt.value === selectedKeys[0];
242
+ return looseMatch ? String(opt.value) === String(selectedKeys[0]) : opt.value === selectedKeys[0];
166
243
  });
167
244
  if (firstSelected && !firstSelected.disabled) {
168
245
  focusedValue = firstSelected.value;
@@ -126,10 +126,27 @@ export interface SelectProps extends DataAttributeProps, Pick<SelectorProps, 'si
126
126
  * @default 200
127
127
  */
128
128
  listHeight?: number;
129
+ /**
130
+ * Number of columns in the dropdown panel.
131
+ * When greater than 1, options are displayed in a grid layout.
132
+ * @default 1
133
+ */
134
+ columns?: number;
135
+ /**
136
+ * Fixed width of the dropdown popup in pixels.
137
+ * When set, the popup will not stretch to match the trigger width.
138
+ * Useful with multi-column layout to provide enough horizontal space.
139
+ */
140
+ popupWidth?: number;
141
+ /**
142
+ * Enable loose value matching. When enabled, value comparison uses
143
+ * `String(a) === String(b)` instead of strict equality (`===`).
144
+ * Resolves type mismatch when options use string values but the
145
+ * controlled value is a number (or vice versa).
146
+ * @default false
147
+ */
148
+ looseMatch?: boolean;
129
149
  }
130
- /**
131
- * Props for the internal Option component (renders a single dropdown option).
132
- */
133
150
  export interface OptionProps {
134
151
  /**
135
152
  * Display label
@@ -165,6 +182,7 @@ export interface OptionsContextValue {
165
182
  selectedKeys: Array<string | number>;
166
183
  focusedKey: string | number;
167
184
  multiple: boolean;
185
+ looseMatch: boolean;
168
186
  onSelect: (key: string | number, e?: React.MouseEvent | React.KeyboardEvent) => void;
169
187
  }
170
188
  /**
@@ -223,6 +241,15 @@ export interface OptionsProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
223
241
  virtualApiRef?: React.MutableRefObject<{
224
242
  scrollToIndex: (index: number) => void;
225
243
  } | null>;
244
+ /**
245
+ * Number of columns in the dropdown grid layout.
246
+ * @default 1
247
+ */
248
+ columns?: number;
249
+ /**
250
+ * Whether loose value matching is enabled
251
+ */
252
+ looseMatch?: boolean;
226
253
  /**
227
254
  * Callback when an option is selected
228
255
  */
@@ -18,6 +18,7 @@ var Option = function Option(props) {
18
18
  selectedKeys = _useContext.selectedKeys,
19
19
  focusedKey = _useContext.focusedKey,
20
20
  multiple = _useContext.multiple,
21
+ looseMatch = _useContext.looseMatch,
21
22
  onSelect = _useContext.onSelect;
22
23
  var handleClick = function handleClick(e) {
23
24
  if (!disabled) {
@@ -25,9 +26,12 @@ var Option = function Option(props) {
25
26
  }
26
27
  };
27
28
  var htmlTitle = title ? title : typeof label === 'string' ? label : undefined;
28
- var selected = selectedKeys.includes(value);
29
+ var selected = looseMatch ? selectedKeys.some(function (k) {
30
+ return String(k) === String(value);
31
+ }) : selectedKeys.includes(value);
32
+ var focused = looseMatch ? String(focusedKey) === String(value) : focusedKey === value;
29
33
  return /*#__PURE__*/React.createElement("div", {
30
- className: clsx("".concat(prefixCls, "-option"), _defineProperty(_defineProperty(_defineProperty({}, "".concat(prefixCls, "-option-selected"), selected), "".concat(prefixCls, "-option-focused"), focusedKey === value), "".concat(prefixCls, "-option-disabled"), disabled)),
34
+ className: clsx("".concat(prefixCls, "-option"), _defineProperty(_defineProperty(_defineProperty({}, "".concat(prefixCls, "-option-selected"), selected), "".concat(prefixCls, "-option-focused"), focused), "".concat(prefixCls, "-option-disabled"), disabled)),
31
35
  "aria-label": "option",
32
36
  onClick: handleClick,
33
37
  onKeyDown: function onKeyDown(e) {
@@ -1,4 +1,10 @@
1
- var _excluded = ["prefixCls", "options", "popupClassName", "popupStyle", "selectedKeys", "focusedKey", "dataTestId", "multiple", "virtual", "itemHeight", "listHeight", "virtualApiRef", "onSelect"];
1
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ var _excluded = ["prefixCls", "options", "popupClassName", "popupStyle", "selectedKeys", "focusedKey", "dataTestId", "multiple", "virtual", "itemHeight", "listHeight", "virtualApiRef", "columns", "looseMatch", "onSelect"];
3
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
7
+ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
2
8
  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); }
3
9
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
4
10
  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; }
@@ -26,20 +32,23 @@ var Options = /*#__PURE__*/forwardRef(function (props, ref) {
26
32
  _props$listHeight = props.listHeight,
27
33
  listHeight = _props$listHeight === void 0 ? 200 : _props$listHeight,
28
34
  virtualApiRef = props.virtualApiRef,
35
+ _props$columns = props.columns,
36
+ columns = _props$columns === void 0 ? 1 : _props$columns,
37
+ _props$looseMatch = props.looseMatch,
38
+ looseMatch = _props$looseMatch === void 0 ? false : _props$looseMatch,
29
39
  onSelect = props.onSelect,
30
40
  rest = _objectWithoutProperties(props, _excluded);
41
+ var multiColumn = columns > 1;
31
42
  var rootRef = useRef(null);
32
- // Forward the popup root element to the parent (Select uses it as the
33
- // keyboard-nav containerRef) while keeping a stable RefObject for the
34
- // effects below.
35
43
  useImperativeHandle(ref, function () {
36
44
  return rootRef.current;
37
45
  }, []);
38
46
 
39
- // Hook is always called (rules of hooks). When `virtual` is off the
40
- // returned viewportRef is never attached, so the observer stays idle.
47
+ // In multi-column virtual mode, each "row" contains `columns` options.
48
+ // The virtual list tracks rows, not individual options.
49
+ var rowCount = multiColumn ? Math.ceil(options.length / columns) : options.length;
41
50
  var _useVirtualList = useVirtualList({
42
- itemCount: options.length,
51
+ itemCount: virtual ? rowCount : 0,
43
52
  itemHeight: itemHeight
44
53
  }),
45
54
  start = _useVirtualList.start,
@@ -48,37 +57,34 @@ var Options = /*#__PURE__*/forwardRef(function (props, ref) {
48
57
  totalHeight = _useVirtualList.totalHeight,
49
58
  viewportRef = _useVirtualList.viewportRef,
50
59
  onScroll = _useVirtualList.onScroll,
51
- scrollToIndex = _useVirtualList.scrollToIndex;
60
+ _scrollToIndex = _useVirtualList.scrollToIndex;
52
61
 
53
- // Expose the virtual scroll API to the parent so keyboard navigation
54
- // can drive the virtual viewport (replaces scrollIntoView).
62
+ // Expose the virtual scroll API to the parent.
63
+ // In multi-column mode, the parent passes a flat option index, so we
64
+ // convert it to a row index before scrolling.
55
65
  useEffect(function () {
56
66
  if (!virtual || !virtualApiRef) return;
57
67
  virtualApiRef.current = {
58
- scrollToIndex: scrollToIndex
68
+ scrollToIndex: function scrollToIndex(flatIndex) {
69
+ var rowIndex = multiColumn ? Math.floor(flatIndex / columns) : flatIndex;
70
+ _scrollToIndex(rowIndex);
71
+ }
59
72
  };
60
73
  return function () {
61
74
  if (virtualApiRef.current) {
62
75
  virtualApiRef.current = null;
63
76
  }
64
77
  };
65
- }, [virtual, virtualApiRef, scrollToIndex]);
78
+ }, [virtual, virtualApiRef, _scrollToIndex, multiColumn, columns]);
66
79
 
67
- // Reset the scroll position whenever the option set changes (e.g. after
68
- // filtering via search), otherwise the viewport may sit at an offset
69
- // where none of the new results are visible.
80
+ // Reset scroll position when options change.
70
81
  useEffect(function () {
71
82
  if (virtual) {
72
- scrollToIndex(0);
83
+ _scrollToIndex(0);
73
84
  }
74
- }, [options, virtual, scrollToIndex]);
85
+ }, [options, virtual, _scrollToIndex]);
75
86
 
76
- // Bring the focused option into view on open and whenever it changes.
77
- // - virtual mode: drive the virtual viewport via scrollToIndex.
78
- // - default mode: scrollIntoView on the option's DOM node.
79
- // Options owns this (rather than the keyboard hook) so it also covers the
80
- // initial open, where the keyboard effect can fire before the popup is
81
- // mounted / the virtual API ref is wired up.
87
+ // Bring the focused option into view.
82
88
  useEffect(function () {
83
89
  if (options.length === 0) return;
84
90
  if (focusedKey === '') return;
@@ -87,7 +93,8 @@ var Options = /*#__PURE__*/forwardRef(function (props, ref) {
87
93
  });
88
94
  if (focusedIndex < 0) return;
89
95
  if (virtual) {
90
- scrollToIndex(focusedIndex);
96
+ var rowIndex = multiColumn ? Math.floor(focusedIndex / columns) : focusedIndex;
97
+ _scrollToIndex(rowIndex);
91
98
  return;
92
99
  }
93
100
  var container = rootRef.current;
@@ -95,21 +102,94 @@ var Options = /*#__PURE__*/forwardRef(function (props, ref) {
95
102
  var optionElements = container.querySelectorAll('[role="option"]');
96
103
  var targetElement = optionElements[focusedIndex];
97
104
  if (targetElement) {
98
- targetElement.scrollIntoView({
99
- block: 'nearest'
100
- });
105
+ // Use manual scroll calculation instead of scrollIntoView to avoid
106
+ // scrolling the entire page (the popup is rendered in a portal).
107
+ var containerRect = container.getBoundingClientRect();
108
+ var targetRect = targetElement.getBoundingClientRect();
109
+ if (targetRect.bottom > containerRect.bottom) {
110
+ container.scrollTop += targetRect.bottom - containerRect.bottom;
111
+ } else if (targetRect.top < containerRect.top) {
112
+ container.scrollTop -= containerRect.top - targetRect.top;
113
+ }
101
114
  }
102
- }, [focusedKey, virtual, options, scrollToIndex, rootRef]);
115
+ }, [focusedKey, virtual, options, _scrollToIndex, rootRef, multiColumn, columns]);
103
116
  var isEmpty = options.length === 0;
104
117
  var ctxValue = {
105
118
  prefixCls: prefixCls,
106
119
  selectedKeys: selectedKeys,
107
120
  focusedKey: focusedKey,
108
121
  multiple: multiple,
122
+ looseMatch: looseMatch,
109
123
  onSelect: onSelect
110
124
  };
111
125
 
112
- // ----- Virtual mode: only render the visible window -----
126
+ // ----- Virtual + multi-column: render rows of N options -----
127
+ if (virtual && multiColumn && !isEmpty) {
128
+ var visibleRows = [];
129
+ for (var rowIdx = start; rowIdx < end; rowIdx++) {
130
+ var rowOptions = [];
131
+ for (var col = 0; col < columns; col++) {
132
+ var flatIdx = rowIdx * columns + col;
133
+ if (flatIdx < options.length) {
134
+ rowOptions.push({
135
+ option: options[flatIdx],
136
+ flatIdx: flatIdx
137
+ });
138
+ }
139
+ }
140
+ visibleRows.push({
141
+ rowIdx: rowIdx,
142
+ rowOptions: rowOptions
143
+ });
144
+ }
145
+ return /*#__PURE__*/React.createElement("div", _extends({}, rest, {
146
+ className: clsx("".concat(prefixCls, "-popup"), "".concat(prefixCls, "-popup-virtual"), popupClassName),
147
+ style: popupStyle,
148
+ ref: rootRef,
149
+ tabIndex: -1
150
+ }), /*#__PURE__*/React.createElement(OptionsCtx.Provider, {
151
+ value: ctxValue
152
+ }, /*#__PURE__*/React.createElement("div", {
153
+ className: "".concat(prefixCls, "-popup-viewport"),
154
+ ref: viewportRef,
155
+ onScroll: onScroll,
156
+ style: {
157
+ maxHeight: listHeight,
158
+ overflowY: 'auto'
159
+ }
160
+ }, /*#__PURE__*/React.createElement("div", {
161
+ style: {
162
+ height: totalHeight,
163
+ position: 'relative'
164
+ }
165
+ }, visibleRows.map(function (_ref, ri) {
166
+ var rowIdx = _ref.rowIdx,
167
+ rowOptions = _ref.rowOptions;
168
+ return /*#__PURE__*/React.createElement("div", {
169
+ key: rowIdx,
170
+ className: "".concat(prefixCls, "-popup-grid-row"),
171
+ style: {
172
+ position: 'absolute',
173
+ top: 0,
174
+ left: 0,
175
+ right: 0,
176
+ height: itemHeight,
177
+ transform: "translateY(".concat(offsetY + ri * itemHeight, "px)"),
178
+ display: 'grid',
179
+ gridTemplateColumns: "repeat(".concat(columns, ", 1fr)")
180
+ }
181
+ }, rowOptions.map(function (_ref2) {
182
+ var option = _ref2.option,
183
+ flatIdx = _ref2.flatIdx;
184
+ return /*#__PURE__*/React.createElement(Option, _extends({}, option, {
185
+ key: option.value,
186
+ dataTestId: "".concat(dataTestId, "-").concat(flatIdx)
187
+ }));
188
+ }));
189
+ })))));
190
+ }
191
+
192
+ // ----- Virtual single-column: render visible window -----
113
193
  if (virtual && !isEmpty) {
114
194
  var visibleOptions = options.slice(start, end);
115
195
  return /*#__PURE__*/React.createElement("div", _extends({}, rest, {
@@ -148,10 +228,12 @@ var Options = /*#__PURE__*/forwardRef(function (props, ref) {
148
228
  })))));
149
229
  }
150
230
 
151
- // ----- Default mode: render every option (unchanged behavior) -----
231
+ // ----- Default mode (non-virtual) -----
152
232
  return /*#__PURE__*/React.createElement("div", _extends({}, rest, {
153
- className: clsx("".concat(prefixCls, "-popup"), popupClassName),
154
- style: popupStyle,
233
+ className: clsx("".concat(prefixCls, "-popup"), _defineProperty({}, "".concat(prefixCls, "-popup-grid"), multiColumn), popupClassName),
234
+ style: _objectSpread(_objectSpread({}, popupStyle), multiColumn ? {
235
+ '--yee-select-columns': columns
236
+ } : undefined),
155
237
  ref: rootRef,
156
238
  tabIndex: -1
157
239
  }), isEmpty ? /*#__PURE__*/React.createElement("div", {