@rainbow-oh/yee-c 0.14.6 → 0.15.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 (65) hide show
  1. package/dist/cjs/Form/Field.js +20 -1
  2. package/dist/cjs/Form/Group.js +3 -1
  3. package/dist/cjs/Form/index.d.ts +1 -1
  4. package/dist/cjs/Input/style/index.less +1 -0
  5. package/dist/cjs/InputNumber/style/index.less +1 -0
  6. package/dist/cjs/Select/hooks/useSelectKeyboard.js +5 -2
  7. package/dist/cjs/Selector/selector.js +6 -3
  8. package/dist/cjs/Selector/style/index.less +2 -2
  9. package/dist/cjs/TableSelect/hooks/useTableSelectKeyboard.d.ts +27 -0
  10. package/dist/cjs/TableSelect/hooks/useTableSelectKeyboard.js +128 -0
  11. package/dist/cjs/TableSelect/interface.d.ts +9 -4
  12. package/dist/cjs/TableSelect/internal-table.js +27 -14
  13. package/dist/cjs/TableSelect/style/index.less +3 -11
  14. package/dist/cjs/TableSelect/table-select.js +40 -3
  15. package/dist/cjs/style/color.css +5 -5
  16. package/dist/cjs/style/color.gemini.css +1 -1
  17. package/dist/cjs/style/size.css +1 -1
  18. package/dist/cjs/style/themes.css +2 -2
  19. package/dist/cjs/style/variables.css +7 -7
  20. package/dist/cjs/version.d.ts +1 -1
  21. package/dist/cjs/version.js +1 -1
  22. package/dist/esm/Form/Field.js +35 -4
  23. package/dist/esm/Form/Group.js +10 -6
  24. package/dist/esm/Form/index.d.ts +1 -1
  25. package/dist/esm/Input/style/index.less +1 -0
  26. package/dist/esm/InputNumber/style/index.less +1 -0
  27. package/dist/esm/Select/hooks/useSelectKeyboard.js +7 -2
  28. package/dist/esm/Selector/selector.js +10 -5
  29. package/dist/esm/Selector/style/index.less +2 -2
  30. package/dist/esm/TableSelect/hooks/useTableSelectKeyboard.d.ts +27 -0
  31. package/dist/esm/TableSelect/hooks/useTableSelectKeyboard.js +132 -0
  32. package/dist/esm/TableSelect/interface.d.ts +9 -4
  33. package/dist/esm/TableSelect/internal-table.js +6 -2
  34. package/dist/esm/TableSelect/style/index.less +3 -11
  35. package/dist/esm/TableSelect/table-select.js +36 -3
  36. package/dist/esm/style/color.css +5 -5
  37. package/dist/esm/style/color.gemini.css +1 -1
  38. package/dist/esm/style/size.css +1 -1
  39. package/dist/esm/style/themes.css +2 -2
  40. package/dist/esm/style/variables.css +7 -7
  41. package/dist/esm/version.d.ts +1 -1
  42. package/dist/esm/version.js +1 -1
  43. package/dist/umd/yee-c.min.css +3 -3
  44. package/dist/umd/yee-c.min.js +1 -1
  45. package/package.json +1 -1
  46. package/src/Form/Field.required.test.tsx +146 -0
  47. package/src/Form/Field.tsx +32 -2
  48. package/src/Form/Group.tsx +7 -2
  49. package/src/Form/index.ts +4 -0
  50. package/src/Input/style/index.less +1 -0
  51. package/src/InputNumber/style/index.less +1 -0
  52. package/src/Select/hooks/useSelectKeyboard.ts +7 -2
  53. package/src/Selector/selector.tsx +10 -5
  54. package/src/Selector/style/index.less +2 -2
  55. package/src/TableSelect/hooks/useTableSelectKeyboard.ts +141 -0
  56. package/src/TableSelect/index.en-US.md +47 -44
  57. package/src/TableSelect/index.zh-CN.md +47 -44
  58. package/src/TableSelect/interface.ts +9 -4
  59. package/src/TableSelect/internal-table.tsx +14 -6
  60. package/src/TableSelect/style/index.less +3 -11
  61. package/src/TableSelect/table-select.tsx +45 -3
  62. package/src/style/color.css +5 -5
  63. package/src/style/color.gemini.css +1 -1
  64. package/src/style/size.css +1 -1
  65. package/src/version.ts +1 -1
@@ -20,7 +20,7 @@ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
20
20
  import clsx from 'clsx';
21
21
  import { HelpCircle } from 'lucide-react';
22
22
  import { AnimatePresence, motion } from 'motion/react';
23
- import React, { useContext, useLayoutEffect, useMemo, useReducer } from 'react';
23
+ import React, { useContext, useLayoutEffect, useMemo, useReducer, useRef } from 'react';
24
24
  import Tooltip from "../Tooltip";
25
25
  import FieldContext from "./FieldContext";
26
26
  import FormContext from "./FormContext";
@@ -56,6 +56,7 @@ var Field = function Field(props) {
56
56
  var _useContext = useContext(FieldContext),
57
57
  getCallbacks = _useContext.getCallbacks,
58
58
  getFieldStatus = _useContext.getFieldStatus,
59
+ getFieldValidate = _useContext.getFieldValidate,
59
60
  getFieldValue = _useContext.getFieldValue,
60
61
  getFieldsValue = _useContext.getFieldsValue,
61
62
  setFieldsValue = _useContext.setFieldsValue,
@@ -84,15 +85,45 @@ var Field = function Field(props) {
84
85
  var normalizedName = useMemo(function () {
85
86
  return stringifyPath(fullFieldNamePath);
86
87
  }, [fullFieldNamePath]);
88
+
89
+ // Registration happens once per field name, but the store (validateField)
90
+ // reads validation config off entity.props on every validation pass — the
91
+ // getter keeps those reads on the latest render's props so runtime changes
92
+ // to required/rules take effect.
93
+ var propsRef = useRef(props);
94
+ propsRef.current = props;
87
95
  useLayoutEffect(function () {
88
96
  var unregister = registerFieldEntities === null || registerFieldEntities === void 0 ? void 0 : registerFieldEntities({
89
- props: _objectSpread(_objectSpread({}, props), {}, {
90
- name: normalizedName
91
- }),
97
+ get props() {
98
+ return _objectSpread(_objectSpread({}, propsRef.current), {}, {
99
+ name: normalizedName
100
+ });
101
+ },
92
102
  onStoreChange: forceUpdate
93
103
  });
94
104
  return unregister;
95
105
  }, [normalizedName, registerFieldEntities]);
106
+
107
+ // JSON signature (not reference) because rules is typically an inline array
108
+ // literal; validator functions are dropped, but the getter above already
109
+ // makes the next natural validation use them.
110
+ var validateSignature = useMemo(function () {
111
+ return JSON.stringify({
112
+ required: required,
113
+ rules: rules
114
+ });
115
+ }, [required, rules]);
116
+ var prevValidateSignatureRef = useRef(validateSignature);
117
+ useLayoutEffect(function () {
118
+ var prev = prevValidateSignatureRef.current;
119
+ prevValidateSignatureRef.current = validateSignature;
120
+ if (prev === validateSignature) return;
121
+ // Only re-check fields already showing a validation message — pristine
122
+ // fields must not start erroring just because rules changed.
123
+ if (getFieldValidate !== null && getFieldValidate !== void 0 && getFieldValidate(normalizedName)) {
124
+ void (validateField === null || validateField === void 0 ? void 0 : validateField(normalizedName));
125
+ }
126
+ }, [validateSignature, normalizedName]);
96
127
  var isRequired = function isRequired() {
97
128
  return Array.isArray(rules) && rules.find(function (rule) {
98
129
  return rule.required;
@@ -57,7 +57,7 @@ var Group = function Group(props) {
57
57
  forceValidate = _useReducer4[1]; // eslint-disable-line @typescript-eslint/no-unused-vars
58
58
 
59
59
  // Group validation
60
- var _validate = /*#__PURE__*/function () {
60
+ var validate = /*#__PURE__*/function () {
61
61
  var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(force) {
62
62
  var values, errors, hasEmpty, _iterator, _step, rule, result, _ref2, _result$message;
63
63
  return _regeneratorRuntime().wrap(function _callee$(_context) {
@@ -165,7 +165,7 @@ var Group = function Group(props) {
165
165
  while (1) switch (_context2.prev = _context2.next) {
166
166
  case 0:
167
167
  _context2.next = 2;
168
- return _validate(false);
168
+ return validate(false);
169
169
  case 2:
170
170
  result = _context2.sent;
171
171
  setErrors(result);
@@ -195,17 +195,21 @@ var Group = function Group(props) {
195
195
  };
196
196
  }, [childNames, formInstance]);
197
197
 
198
- // Register group validator to FormStore
198
+ // Register group validator to FormStore. The closure is registered once, so
199
+ // it reads the latest validate (and its required/rules/childNames captures)
200
+ // through a ref — otherwise runtime prop changes never take effect.
201
+ var validateRef = useRef(validate);
202
+ validateRef.current = validate;
199
203
  useEffect(function () {
200
204
  var unregister = formInstance.registerGroupEntity(groupId, {
201
205
  validate: function () {
202
- var _validate2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(force) {
206
+ var _validate = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee3(force) {
203
207
  var result;
204
208
  return _regeneratorRuntime().wrap(function _callee3$(_context3) {
205
209
  while (1) switch (_context3.prev = _context3.next) {
206
210
  case 0:
207
211
  _context3.next = 2;
208
- return _validate(force);
212
+ return validateRef.current(force);
209
213
  case 2:
210
214
  result = _context3.sent;
211
215
  setErrors(result);
@@ -218,7 +222,7 @@ var Group = function Group(props) {
218
222
  }, _callee3);
219
223
  }));
220
224
  function validate(_x2) {
221
- return _validate2.apply(this, arguments);
225
+ return _validate.apply(this, arguments);
222
226
  }
223
227
  return validate;
224
228
  }(),
@@ -16,5 +16,5 @@ type FormInterface = typeof InternalForm & {
16
16
  List: typeof List;
17
17
  };
18
18
  declare const Form: FormInterface;
19
- export type { FieldGroupProps, FieldStatus, FieldStatusData, FormInstance, FormListProps, FormProps, Rule as FormRule, ValidateMessage, } from './interface';
19
+ export type { FieldGroupProps, FieldProps, FieldStatus, FieldStatusData, FieldTooltip, FormInstance, FormListField, FormListOperation, FormListProps, FormProps, Rule as FormRule, ValidateMessage, } from './interface';
20
20
  export default Form;
@@ -45,6 +45,7 @@
45
45
  box-shadow: none;
46
46
  height: auto;
47
47
  padding: 0;
48
+ border-radius: 0;
48
49
  }
49
50
 
50
51
  &-inner {
@@ -13,6 +13,7 @@
13
13
  border-radius: var(--yee-input-border-radius, 4px);
14
14
  background-color: var(--yee-input-content-bg-color);
15
15
  transition: all 0.3s;
16
+ overflow: hidden;
16
17
 
17
18
  &:not(@{pls}-disabled):hover {
18
19
  border-color: var(--yee-input-hover-border-color);
@@ -225,8 +225,13 @@ export default function useSelectKeyboard(_ref) {
225
225
  handleEnter();
226
226
  break;
227
227
  case 'Escape':
228
- event.preventDefault();
229
- handleEscape();
228
+ // Only swallow Escape while open so outer layers (e.g. a Dialog's
229
+ // useEsc listening on document) still receive it when closed.
230
+ if (open) {
231
+ event.preventDefault();
232
+ event.stopPropagation();
233
+ handleEscape();
234
+ }
230
235
  break;
231
236
  default:
232
237
  }
@@ -220,12 +220,17 @@ var Selector = /*#__PURE__*/React.forwardRef(function (props, ref) {
220
220
  "aria-disabled": disabled || undefined,
221
221
  onKeyDown: function onKeyDown(event) {
222
222
  if (disabled) return;
223
- // For non-searchable single select, handle Enter to open dropdown
224
- if (!searchable && event.key === 'Enter' && !open) {
225
- onOpenChange === null || onOpenChange === void 0 || onOpenChange(true);
223
+ // Enter opens the dropdown while it is closed
224
+ if (event.key === 'Enter' && !open && onOpenChange) {
225
+ event.preventDefault();
226
+ onOpenChange(true);
227
+ }
228
+ // Input forwards this handler for its own keydowns; handling them
229
+ // here too would fire the handler twice per keypress. Only handle
230
+ // events that originate on the container (non-searchable focus).
231
+ if (event.target !== inputRef.current) {
232
+ externalOnKeyDown === null || externalOnKeyDown === void 0 || externalOnKeyDown(event);
226
233
  }
227
- // Also call external onKeyDown if provided
228
- externalOnKeyDown === null || externalOnKeyDown === void 0 || externalOnKeyDown(event);
229
234
  },
230
235
  ref: ref
231
236
  }), /*#__PURE__*/React.createElement("div", {
@@ -54,8 +54,8 @@
54
54
  height: var(--yee-selector-large-height);
55
55
  }
56
56
 
57
- &:focus-within,
58
- &:focus {
57
+ &:not(@{pls}-disabled):focus-within,
58
+ &:not(@{pls}-disabled):focus {
59
59
  @{pls}-input-wrapper {
60
60
  z-index: 1;
61
61
  opacity: var(--yee-selector-input-focus-opacity);
@@ -0,0 +1,27 @@
1
+ /// <reference types="react" />
2
+ import type { TableSelectDataType } from '../interface';
3
+ type KeyType = string | number;
4
+ interface UseTableSelectKeyboardParams {
5
+ open: boolean;
6
+ disabled?: boolean;
7
+ dataSource: Array<TableSelectDataType>;
8
+ rowKey: string | ((record: any) => string);
9
+ selectionType: 'radio' | 'checkbox';
10
+ selectedRowKeys: Array<KeyType>;
11
+ searchValue: string;
12
+ onSelect: (keys: Array<KeyType>, rows: Array<TableSelectDataType>) => void;
13
+ onClose: () => void;
14
+ }
15
+ /**
16
+ * Keyboard navigation for the popup table. The highlight is positional (like a
17
+ * hover cursor): `activeRow` is a rendered row index, and Space selects the
18
+ * record displayed there. The rendered order is the filter→sort→paginate
19
+ * result inside Table and is not derivable from `dataSource`, so the row count
20
+ * and the highlighted row's key are both read from the popup DOM.
21
+ */
22
+ export default function useTableSelectKeyboard({ open, disabled, dataSource, rowKey, selectionType, selectedRowKeys, searchValue, onSelect, onClose, }: UseTableSelectKeyboardParams): {
23
+ popupRef: import("react").RefObject<HTMLDivElement | null>;
24
+ activeRow: number;
25
+ handleKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void;
26
+ };
27
+ export {};
@@ -0,0 +1,132 @@
1
+ function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
2
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
+ function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
4
+ function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
5
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
6
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
7
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
8
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
9
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
10
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
11
+ import { useEffect, useRef, useState } from 'react';
12
+ /**
13
+ * Keyboard navigation for the popup table. The highlight is positional (like a
14
+ * hover cursor): `activeRow` is a rendered row index, and Space selects the
15
+ * record displayed there. The rendered order is the filter→sort→paginate
16
+ * result inside Table and is not derivable from `dataSource`, so the row count
17
+ * and the highlighted row's key are both read from the popup DOM.
18
+ */
19
+ export default function useTableSelectKeyboard(_ref) {
20
+ var open = _ref.open,
21
+ disabled = _ref.disabled,
22
+ dataSource = _ref.dataSource,
23
+ rowKey = _ref.rowKey,
24
+ selectionType = _ref.selectionType,
25
+ selectedRowKeys = _ref.selectedRowKeys,
26
+ searchValue = _ref.searchValue,
27
+ onSelect = _ref.onSelect,
28
+ onClose = _ref.onClose;
29
+ var popupRef = useRef(null);
30
+ // 1-based index of the highlighted row (`tr[data-row-index]`), 0 = none.
31
+ var _useState = useState(0),
32
+ _useState2 = _slicedToArray(_useState, 2),
33
+ activeRow = _useState2[0],
34
+ setActiveRow = _useState2[1];
35
+ useEffect(function () {
36
+ setActiveRow(0);
37
+ }, [open, searchValue]);
38
+ var getRecordKey = function getRecordKey(item) {
39
+ return typeof rowKey === 'function' ? rowKey(item) : item[rowKey];
40
+ };
41
+ var getRowCount = function getRowCount() {
42
+ var _popupRef$current$que, _popupRef$current;
43
+ return (_popupRef$current$que = (_popupRef$current = popupRef.current) === null || _popupRef$current === void 0 ? void 0 : _popupRef$current.querySelectorAll('tr[data-row-index]').length) !== null && _popupRef$current$que !== void 0 ? _popupRef$current$que : 0;
44
+ };
45
+ useEffect(function () {
46
+ var _popupRef$current2;
47
+ if (!open || activeRow < 1) return;
48
+ var rowEl = (_popupRef$current2 = popupRef.current) === null || _popupRef$current2 === void 0 ? void 0 : _popupRef$current2.querySelector("tr[data-row-index=\"".concat(activeRow, "\"]"));
49
+ if (!rowEl) return;
50
+ // Manual scroll instead of scrollIntoView: the popup renders in a portal,
51
+ // and scrollIntoView would scroll the page as well as the popup.
52
+ var parent = rowEl.parentElement;
53
+ while (parent && parent !== popupRef.current) {
54
+ var _window$getComputedSt = window.getComputedStyle(parent),
55
+ overflowY = _window$getComputedSt.overflowY;
56
+ if (/(auto|scroll)/.test(overflowY) && parent.scrollHeight > parent.clientHeight) {
57
+ var parentRect = parent.getBoundingClientRect();
58
+ var rowRect = rowEl.getBoundingClientRect();
59
+ if (rowRect.bottom > parentRect.bottom) {
60
+ parent.scrollTop += rowRect.bottom - parentRect.bottom;
61
+ } else if (rowRect.top < parentRect.top) {
62
+ parent.scrollTop -= parentRect.top - rowRect.top;
63
+ }
64
+ return;
65
+ }
66
+ parent = parent.parentElement;
67
+ }
68
+ }, [activeRow, open]);
69
+ var selectActiveRow = function selectActiveRow() {
70
+ var _popupRef$current3;
71
+ var rowEl = (_popupRef$current3 = popupRef.current) === null || _popupRef$current3 === void 0 ? void 0 : _popupRef$current3.querySelector("tr[data-row-index=\"".concat(activeRow, "\"]"));
72
+ var inputEl = rowEl === null || rowEl === void 0 ? void 0 : rowEl.querySelector('input[type="radio"],input[type="checkbox"]');
73
+ if (!inputEl || inputEl.disabled) return;
74
+ var record = dataSource.find(function (item) {
75
+ return String(getRecordKey(item)) === inputEl.value;
76
+ });
77
+ if (!record) return;
78
+ var key = getRecordKey(record);
79
+ if (selectionType === 'radio') {
80
+ // Same shape as the click path: arrays, normalized by the consumer.
81
+ onSelect([key], [record]);
82
+ onClose();
83
+ return;
84
+ }
85
+ var checked = selectedRowKeys.some(function (k) {
86
+ return String(k) === String(key);
87
+ });
88
+ var nextKeys = checked ? selectedRowKeys.filter(function (k) {
89
+ return String(k) !== String(key);
90
+ }) : [].concat(_toConsumableArray(selectedRowKeys), [key]);
91
+ var rows = dataSource.filter(function (item) {
92
+ return nextKeys.some(function (k) {
93
+ return String(getRecordKey(item)) === String(k);
94
+ });
95
+ });
96
+ onSelect(nextKeys, rows);
97
+ };
98
+ var handleKeyDown = function handleKeyDown(event) {
99
+ if (!open || disabled) return;
100
+ switch (event.key) {
101
+ case 'ArrowDown':
102
+ event.preventDefault();
103
+ setActiveRow(Math.min(activeRow + 1, getRowCount()));
104
+ break;
105
+ case 'ArrowUp':
106
+ event.preventDefault();
107
+ setActiveRow(activeRow <= 1 ? getRowCount() : activeRow - 1);
108
+ break;
109
+ case ' ':
110
+ // With nothing highlighted, space keeps its native input behavior.
111
+ if (activeRow > 0) {
112
+ event.preventDefault();
113
+ selectActiveRow();
114
+ }
115
+ break;
116
+ case 'Escape':
117
+ event.preventDefault();
118
+ // Stop the event from reaching outer layers (e.g. a Dialog's useEsc
119
+ // listening on document) so only the dropdown closes.
120
+ event.stopPropagation();
121
+ onClose();
122
+ break;
123
+ default:
124
+ break;
125
+ }
126
+ };
127
+ return {
128
+ popupRef: popupRef,
129
+ activeRow: activeRow,
130
+ handleKeyDown: handleKeyDown
131
+ };
132
+ }
@@ -9,7 +9,7 @@ type SelectedKeyType = string | number;
9
9
  /**
10
10
  * Data item type
11
11
  */
12
- type InDataType = {
12
+ export type TableSelectDataType = {
13
13
  [propName: string]: any;
14
14
  };
15
15
  /**
@@ -40,7 +40,7 @@ export interface InternalTableProps {
40
40
  /**
41
41
  * Data source
42
42
  */
43
- dataSource: Array<InDataType>;
43
+ dataSource: Array<TableSelectDataType>;
44
44
  /**
45
45
  * Custom class name
46
46
  */
@@ -65,6 +65,11 @@ export interface InternalTableProps {
65
65
  * Whether popup is visible (for focus trap)
66
66
  */
67
67
  visible?: boolean;
68
+ /**
69
+ * Ref to the popup root element, used by TableSelect for keyboard navigation
70
+ * @internal
71
+ */
72
+ popupRef?: React.RefObject<HTMLDivElement | null>;
68
73
  }
69
74
  /**
70
75
  * TableSelect component Props interface
@@ -77,7 +82,7 @@ export interface TableSelectProps extends Omit<InternalTableProps, 'value' | 'on
77
82
  /**
78
83
  * Data source
79
84
  */
80
- dataSource: Array<InDataType>;
85
+ dataSource: Array<TableSelectDataType>;
81
86
  /**
82
87
  * Single or multiple selection
83
88
  * @default: 'radio'
@@ -161,7 +166,7 @@ export interface TableSelectProps extends Omit<InternalTableProps, 'value' | 'on
161
166
  * Option label property (text field for displaying labels)
162
167
  * @default 'name'
163
168
  */
164
- optionLabelProp?: string | ((option: InDataType) => string);
169
+ optionLabelProp?: string | ((option: TableSelectDataType) => string);
165
170
  /**
166
171
  * Selection change callback
167
172
  * @param keys Selected keys
@@ -1,5 +1,5 @@
1
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", "type", "value", "columns", "dataSource", "rowKey", "isShowFilter", "rowSelection", "visible"];
2
+ var _excluded = ["prefixCls", "type", "value", "columns", "dataSource", "rowKey", "isShowFilter", "rowSelection", "visible", "popupRef"];
3
3
  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); }
4
4
  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; }
5
5
  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; }
@@ -32,6 +32,7 @@ var InternalTable = function InternalTable(props) {
32
32
  _props$rowSelection = props.rowSelection,
33
33
  rowSelection = _props$rowSelection === void 0 ? {} : _props$rowSelection,
34
34
  visible = props.visible,
35
+ externalPopupRef = props.popupRef,
35
36
  rest = _objectWithoutProperties(props, _excluded);
36
37
  var _useState = useState([]),
37
38
  _useState2 = _slicedToArray(_useState, 2),
@@ -77,7 +78,10 @@ var InternalTable = function InternalTable(props) {
77
78
  return /*#__PURE__*/React.createElement("div", {
78
79
  className: "".concat(prefixCls, "-popup"),
79
80
  tabIndex: 0,
80
- ref: popupRef
81
+ ref: function ref(node) {
82
+ popupRef.current = node;
83
+ if (externalPopupRef) externalPopupRef.current = node;
84
+ }
81
85
  }, renderRadio(), /*#__PURE__*/React.createElement(Table, _extends({}, rest, {
82
86
  rowKey: rowKey,
83
87
  dataSource: internalDataSource,
@@ -6,16 +6,8 @@
6
6
  margin: 0;
7
7
  padding: 12px 0;
8
8
 
9
- .rb-table-select__radio {
10
- padding: 0 12px;
11
- margin-bottom: 16px;
12
- }
13
- }
14
-
15
- .rb-table-select {
16
- margin-bottom: 0;
17
-
18
- .rb-table-footer-operate {
19
- padding: 0 12px;
9
+ // Keyboard-navigated row (TableSelect arrow keys)
10
+ .yee-table-highlight-row {
11
+ background-color: var(--yee-table-tbody-tr-hover-bg-color);
20
12
  }
21
13
  }
@@ -27,10 +27,14 @@ import Trigger from "../Trigger";
27
27
  import mergeContextToProps from "../utils/mergeContextToProps";
28
28
  import omit from "../utils/omit";
29
29
  import pick from "../utils/pick";
30
+ import useTableSelectKeyboard from "./hooks/useTableSelectKeyboard";
30
31
  import InternalTable from "./internal-table";
31
32
  import "./style/index.less";
32
33
  var handleState = function handleState(value) {
33
- if (typeof value === 'string' || typeof value === 'number') {
34
+ // '' is the empty-value convention for radio; it must not become a key.
35
+ if (value === '') {
36
+ return [];
37
+ } else if (typeof value === 'string' || typeof value === 'number') {
34
38
  return [value];
35
39
  } else if (Array.isArray(value)) {
36
40
  return value;
@@ -140,8 +144,28 @@ var TableSelect = function TableSelect(baseprops) {
140
144
  var renderKeys = keys.length === 0 ? selectionType === 'checkbox' ? [] : '' : keys;
141
145
  onChange === null || onChange === void 0 || onChange(renderKeys, undefined);
142
146
  };
147
+ var _useTableSelectKeyboa = useTableSelectKeyboard({
148
+ open: open,
149
+ disabled: disabled,
150
+ dataSource: dataSource,
151
+ rowKey: rowKey,
152
+ selectionType: selectionType,
153
+ selectedRowKeys: mergedValue,
154
+ searchValue: searchValue,
155
+ onSelect: onSelectChange,
156
+ onClose: function onClose() {
157
+ return onVisibleChange(false);
158
+ }
159
+ }),
160
+ popupRef = _useTableSelectKeyboa.popupRef,
161
+ activeRow = _useTableSelectKeyboa.activeRow,
162
+ handleSelectorKeyDown = _useTableSelectKeyboa.handleKeyDown;
163
+ var userHighlight = rest.highlight;
143
164
  var tableProps = omit(_objectSpread(_objectSpread({}, rest), {}, {
144
165
  prefixCls: prefixCls,
166
+ // Keyboard navigation reuses the row highlight; it takes precedence over
167
+ // a user-supplied index list while active.
168
+ highlight: activeRow > 0 ? [activeRow].concat(_toConsumableArray(Array.isArray(userHighlight) ? userHighlight : [])) : userHighlight,
145
169
  rowSelection: _objectSpread(_objectSpread({}, props.rowSelection), {}, {
146
170
  onChange: onSelectChange,
147
171
  selectedRowKeys: mergedValue
@@ -158,8 +182,15 @@ var TableSelect = function TableSelect(baseprops) {
158
182
  selectedKeys: mergedValue,
159
183
  onSearch: handleSearch,
160
184
  onRemove: onTagRemove,
185
+ onClear: function onClear() {
186
+ setMergedValue([]);
187
+ onChange === null || onChange === void 0 || onChange(selectionType === 'radio' ? '' : [], undefined);
188
+ },
189
+ onKeyDown: handleSelectorKeyDown,
161
190
  onOpenChange: function onOpenChange(open) {
162
- if (!open) {
191
+ if (open) {
192
+ onVisibleChange(true);
193
+ } else {
163
194
  clearSearchValue();
164
195
  }
165
196
  },
@@ -171,11 +202,13 @@ var TableSelect = function TableSelect(baseprops) {
171
202
  var renderPopup = function renderPopup() {
172
203
  return /*#__PURE__*/React.createElement(InternalTable, _extends({}, tableProps, {
173
204
  value: value,
174
- rowKey: rowKey
205
+ rowKey: rowKey,
206
+ popupRef: popupRef
175
207
  }));
176
208
  };
177
209
  return /*#__PURE__*/React.createElement(Trigger, _extends({}, rest, {
178
210
  trigger: "click",
211
+ open: open,
179
212
  popupClassName: "".concat(prefixCls, "-popup"),
180
213
  popup: renderPopup(),
181
214
  placement: placement,
@@ -29,7 +29,7 @@
29
29
  --yee-color-text: #39364e;
30
30
  --yee-color-text-secondary: #696969;
31
31
  --yee-color-text-description: #7c7c7c;
32
- --yee-color-text-disabled: #bfbfbf;
32
+ --yee-color-text-disabled: #b8b8b8;
33
33
  --yee-color-text-highlight: #f58220;
34
34
  --yee-color-text-auxiliary: #d1d1d1;
35
35
  --yee-color-split: #e7e7e7;
@@ -37,7 +37,7 @@
37
37
  --yee-color-border-hover: #75b9f0;
38
38
  --yee-color-border-focus: #75b9f0;
39
39
  --yee-color-bg-highlight: rgba(245, 130, 32, 16%);
40
- --yee-color-bg-disabled: #f5f8fa;
40
+ --yee-color-bg-disabled: #f5f5f5;
41
41
  --yee-color-bg-head: #f8f8f8;
42
42
  --yee-color-bg-rail: #f5f5f5;
43
43
  --yee-color-bg-fill: #ededed;
@@ -493,7 +493,7 @@
493
493
  --yee-checkbox-checked-after-border-color: #fff;
494
494
  --yee-checkbox-disabled-unchecked-bg-color: var(--yee-color-bg-disabled);
495
495
  --yee-checkbox-disabled-checked-bg-color: var(--yee-color-bg-disabled);
496
- --yee-checkbox-disabled-checked-border-color: var(--yee-color-border);
496
+ --yee-checkbox-disabled-checked-border-color: var(--yee-color-text-disabled);
497
497
  --yee-checkbox-disabled-checked-after-border-color: var(
498
498
  --yee-color-text-disabled
499
499
  );
@@ -1223,7 +1223,7 @@
1223
1223
  --yee-border-color-hover-focus: var(--yee-color-border-focus);
1224
1224
  --yee-placeholder-color: var(--yee-color-placeholder);
1225
1225
 
1226
- /* deprecated:2026-09-11 hover/focus 拆分为 --yee-color-border-hover / --yee-color-border-focus */
1226
+ /* deprecated:2026-09-11 hover/focus 拆分为 --yee-color-border-hover / --yee-color-border-focus */
1227
1227
  --yee-color-border-hover-focus: var(--yee-color-border-focus);
1228
1228
  /* deprecated:2026-09-11 更名 --yee-input-focus-border-color */
1229
1229
  --yee-input-content-focus-border-color: var(--yee-input-focus-border-color);
@@ -1339,5 +1339,5 @@
1339
1339
  --yee-timepicker-bg-color: var(--yee-time-picker-bg-color);
1340
1340
  --yee-treeselect-dropdown-bg-color: var(--yee-tree-select-dropdown-bg-color);
1341
1341
 
1342
- /* deprecated-aliases:end */
1342
+ /* deprecated-aliases:end */
1343
1343
  }
@@ -79,7 +79,7 @@
79
79
  /* ================= 背景 ================= */
80
80
  --yee-color-bg-layout: #f1f2f4;
81
81
  --yee-color-bg-head: #fff;
82
- --yee-color-bg-disabled: #f2f3f5;
82
+ --yee-color-bg-disabled: #f5f5f5;
83
83
  /* 衍生:规范 --hover(极浅悬浮底) */
84
84
  --yee-color-bg-hover: rgba(109, 93, 252, 3.5%);
85
85
 
@@ -587,7 +587,7 @@
587
587
  --yee-select-popup-max-height: 200px;
588
588
 
589
589
  /* --Selector-- */
590
- --yee-selector-padding: 2px 6px;
590
+ --yee-selector-padding: 3px 6px;
591
591
  --yee-selector-border-width: 1px;
592
592
  --yee-selector-border-radius: 4px;
593
593
  --yee-selector-height: var(--yee-input-height);
@@ -1,7 +1,7 @@
1
1
  /*!
2
2
  * @rainbow-oh/yee-c CSS Variables
3
3
  * Auto-generated, do not edit manually
4
- * Generated on 2026-10-10T06:48:35.780Z
4
+ * Generated on 2026-10-15T09:51:26.325Z
5
5
  */
6
6
 
7
7
  /* ========== color.blue.css ========== */
@@ -3846,7 +3846,7 @@
3846
3846
  /* ================= 背景 ================= */
3847
3847
  --yee-color-bg-layout: #f1f2f4;
3848
3848
  --yee-color-bg-head: #fff;
3849
- --yee-color-bg-disabled: #f2f3f5;
3849
+ --yee-color-bg-disabled: #f5f5f5;
3850
3850
  /* 衍生:规范 --hover(极浅悬浮底) */
3851
3851
  --yee-color-bg-hover: rgba(109, 93, 252, 3.5%);
3852
3852