@rainbow-oh/yee-c 0.14.7 → 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 (49) hide show
  1. package/dist/cjs/Form/index.d.ts +1 -1
  2. package/dist/cjs/Input/style/index.less +1 -0
  3. package/dist/cjs/InputNumber/style/index.less +1 -0
  4. package/dist/cjs/Select/hooks/useSelectKeyboard.js +5 -2
  5. package/dist/cjs/Selector/selector.js +6 -3
  6. package/dist/cjs/TableSelect/hooks/useTableSelectKeyboard.d.ts +27 -0
  7. package/dist/cjs/TableSelect/hooks/useTableSelectKeyboard.js +128 -0
  8. package/dist/cjs/TableSelect/interface.d.ts +9 -4
  9. package/dist/cjs/TableSelect/internal-table.js +27 -14
  10. package/dist/cjs/TableSelect/style/index.less +3 -11
  11. package/dist/cjs/TableSelect/table-select.js +40 -3
  12. package/dist/cjs/style/size.css +1 -1
  13. package/dist/cjs/style/themes.css +1 -1
  14. package/dist/cjs/style/variables.css +2 -2
  15. package/dist/cjs/version.d.ts +1 -1
  16. package/dist/cjs/version.js +1 -1
  17. package/dist/esm/Form/index.d.ts +1 -1
  18. package/dist/esm/Input/style/index.less +1 -0
  19. package/dist/esm/InputNumber/style/index.less +1 -0
  20. package/dist/esm/Select/hooks/useSelectKeyboard.js +7 -2
  21. package/dist/esm/Selector/selector.js +10 -5
  22. package/dist/esm/TableSelect/hooks/useTableSelectKeyboard.d.ts +27 -0
  23. package/dist/esm/TableSelect/hooks/useTableSelectKeyboard.js +132 -0
  24. package/dist/esm/TableSelect/interface.d.ts +9 -4
  25. package/dist/esm/TableSelect/internal-table.js +6 -2
  26. package/dist/esm/TableSelect/style/index.less +3 -11
  27. package/dist/esm/TableSelect/table-select.js +36 -3
  28. package/dist/esm/style/size.css +1 -1
  29. package/dist/esm/style/themes.css +1 -1
  30. package/dist/esm/style/variables.css +2 -2
  31. package/dist/esm/version.d.ts +1 -1
  32. package/dist/esm/version.js +1 -1
  33. package/dist/umd/yee-c.min.css +2 -2
  34. package/dist/umd/yee-c.min.js +1 -1
  35. package/package.json +1 -1
  36. package/src/Form/index.ts +4 -0
  37. package/src/Input/style/index.less +1 -0
  38. package/src/InputNumber/style/index.less +1 -0
  39. package/src/Select/hooks/useSelectKeyboard.ts +7 -2
  40. package/src/Selector/selector.tsx +10 -5
  41. package/src/TableSelect/hooks/useTableSelectKeyboard.ts +141 -0
  42. package/src/TableSelect/index.en-US.md +47 -44
  43. package/src/TableSelect/index.zh-CN.md +47 -44
  44. package/src/TableSelect/interface.ts +9 -4
  45. package/src/TableSelect/internal-table.tsx +14 -6
  46. package/src/TableSelect/style/index.less +3 -11
  47. package/src/TableSelect/table-select.tsx +45 -3
  48. package/src/style/size.css +1 -1
  49. package/src/version.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rainbow-oh/yee-c",
3
- "version": "0.14.7",
3
+ "version": "0.15.0",
4
4
  "description": "yee basic components",
5
5
  "keywords": [
6
6
  "react",
package/src/Form/index.ts CHANGED
@@ -29,9 +29,13 @@ Form.List = List;
29
29
 
30
30
  export type {
31
31
  FieldGroupProps,
32
+ FieldProps,
32
33
  FieldStatus,
33
34
  FieldStatusData,
35
+ FieldTooltip,
34
36
  FormInstance,
37
+ FormListField,
38
+ FormListOperation,
35
39
  FormListProps,
36
40
  FormProps,
37
41
  Rule as FormRule,
@@ -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);
@@ -261,8 +261,13 @@ export default function useSelectKeyboard({
261
261
  handleEnter();
262
262
  break;
263
263
  case 'Escape':
264
- event.preventDefault();
265
- handleEscape();
264
+ // Only swallow Escape while open so outer layers (e.g. a Dialog's
265
+ // useEsc listening on document) still receive it when closed.
266
+ if (open) {
267
+ event.preventDefault();
268
+ event.stopPropagation();
269
+ handleEscape();
270
+ }
266
271
  break;
267
272
  default:
268
273
  }
@@ -284,12 +284,17 @@ const Selector = React.forwardRef(
284
284
  aria-disabled={disabled || undefined}
285
285
  onKeyDown={(event) => {
286
286
  if (disabled) return;
287
- // For non-searchable single select, handle Enter to open dropdown
288
- if (!searchable && event.key === 'Enter' && !open) {
289
- onOpenChange?.(true);
287
+ // Enter opens the dropdown while it is closed
288
+ if (event.key === 'Enter' && !open && onOpenChange) {
289
+ event.preventDefault();
290
+ onOpenChange(true);
291
+ }
292
+ // Input forwards this handler for its own keydowns; handling them
293
+ // here too would fire the handler twice per keypress. Only handle
294
+ // events that originate on the container (non-searchable focus).
295
+ if (event.target !== inputRef.current) {
296
+ externalOnKeyDown?.(event as React.KeyboardEvent<HTMLInputElement>);
290
297
  }
291
- // Also call external onKeyDown if provided
292
- externalOnKeyDown?.(event as React.KeyboardEvent<HTMLInputElement>);
293
298
  }}
294
299
  ref={ref}
295
300
  >
@@ -0,0 +1,141 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import type { TableSelectDataType } from '../interface';
3
+
4
+ type KeyType = string | number;
5
+
6
+ interface UseTableSelectKeyboardParams {
7
+ open: boolean;
8
+ disabled?: boolean;
9
+ dataSource: Array<TableSelectDataType>;
10
+ rowKey: string | ((record: any) => string);
11
+ selectionType: 'radio' | 'checkbox';
12
+ selectedRowKeys: Array<KeyType>;
13
+ searchValue: string;
14
+ onSelect: (keys: Array<KeyType>, rows: Array<TableSelectDataType>) => void;
15
+ onClose: () => void;
16
+ }
17
+
18
+ /**
19
+ * Keyboard navigation for the popup table. The highlight is positional (like a
20
+ * hover cursor): `activeRow` is a rendered row index, and Space selects the
21
+ * record displayed there. The rendered order is the filter→sort→paginate
22
+ * result inside Table and is not derivable from `dataSource`, so the row count
23
+ * and the highlighted row's key are both read from the popup DOM.
24
+ */
25
+ export default function useTableSelectKeyboard({
26
+ open,
27
+ disabled,
28
+ dataSource,
29
+ rowKey,
30
+ selectionType,
31
+ selectedRowKeys,
32
+ searchValue,
33
+ onSelect,
34
+ onClose,
35
+ }: UseTableSelectKeyboardParams) {
36
+ const popupRef = useRef<HTMLDivElement | null>(null);
37
+ // 1-based index of the highlighted row (`tr[data-row-index]`), 0 = none.
38
+ const [activeRow, setActiveRow] = useState(0);
39
+
40
+ useEffect(() => {
41
+ setActiveRow(0);
42
+ }, [open, searchValue]);
43
+
44
+ const getRecordKey = (item: TableSelectDataType) =>
45
+ typeof rowKey === 'function' ? rowKey(item) : item[rowKey];
46
+
47
+ const getRowCount = () =>
48
+ popupRef.current?.querySelectorAll('tr[data-row-index]').length ?? 0;
49
+
50
+ useEffect(() => {
51
+ if (!open || activeRow < 1) return;
52
+ const rowEl = popupRef.current?.querySelector(
53
+ `tr[data-row-index="${activeRow}"]`,
54
+ ) as HTMLElement | null;
55
+ if (!rowEl) return;
56
+ // Manual scroll instead of scrollIntoView: the popup renders in a portal,
57
+ // and scrollIntoView would scroll the page as well as the popup.
58
+ let parent = rowEl.parentElement;
59
+ while (parent && parent !== popupRef.current) {
60
+ const { overflowY } = window.getComputedStyle(parent);
61
+ if (
62
+ /(auto|scroll)/.test(overflowY) &&
63
+ parent.scrollHeight > parent.clientHeight
64
+ ) {
65
+ const parentRect = parent.getBoundingClientRect();
66
+ const rowRect = rowEl.getBoundingClientRect();
67
+ if (rowRect.bottom > parentRect.bottom) {
68
+ parent.scrollTop += rowRect.bottom - parentRect.bottom;
69
+ } else if (rowRect.top < parentRect.top) {
70
+ parent.scrollTop -= parentRect.top - rowRect.top;
71
+ }
72
+ return;
73
+ }
74
+ parent = parent.parentElement;
75
+ }
76
+ }, [activeRow, open]);
77
+
78
+ const selectActiveRow = () => {
79
+ const rowEl = popupRef.current?.querySelector(
80
+ `tr[data-row-index="${activeRow}"]`,
81
+ );
82
+ const inputEl = rowEl?.querySelector(
83
+ 'input[type="radio"],input[type="checkbox"]',
84
+ ) as HTMLInputElement | null;
85
+ if (!inputEl || inputEl.disabled) return;
86
+
87
+ const record = dataSource.find(
88
+ (item) => String(getRecordKey(item)) === inputEl.value,
89
+ );
90
+ if (!record) return;
91
+ const key = getRecordKey(record);
92
+
93
+ if (selectionType === 'radio') {
94
+ // Same shape as the click path: arrays, normalized by the consumer.
95
+ onSelect([key], [record]);
96
+ onClose();
97
+ return;
98
+ }
99
+
100
+ const checked = selectedRowKeys.some((k) => String(k) === String(key));
101
+ const nextKeys = checked
102
+ ? selectedRowKeys.filter((k) => String(k) !== String(key))
103
+ : [...selectedRowKeys, key];
104
+ const rows = dataSource.filter((item) =>
105
+ nextKeys.some((k) => String(getRecordKey(item)) === String(k)),
106
+ );
107
+ onSelect(nextKeys, rows);
108
+ };
109
+
110
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
111
+ if (!open || disabled) return;
112
+ switch (event.key) {
113
+ case 'ArrowDown':
114
+ event.preventDefault();
115
+ setActiveRow(Math.min(activeRow + 1, getRowCount()));
116
+ break;
117
+ case 'ArrowUp':
118
+ event.preventDefault();
119
+ setActiveRow(activeRow <= 1 ? getRowCount() : activeRow - 1);
120
+ break;
121
+ case ' ':
122
+ // With nothing highlighted, space keeps its native input behavior.
123
+ if (activeRow > 0) {
124
+ event.preventDefault();
125
+ selectActiveRow();
126
+ }
127
+ break;
128
+ case 'Escape':
129
+ event.preventDefault();
130
+ // Stop the event from reaching outer layers (e.g. a Dialog's useEsc
131
+ // listening on document) so only the dropdown closes.
132
+ event.stopPropagation();
133
+ onClose();
134
+ break;
135
+ default:
136
+ break;
137
+ }
138
+ };
139
+
140
+ return { popupRef, activeRow, handleKeyDown };
141
+ }
@@ -31,45 +31,45 @@ A component for selecting one or more options from table data, combining feature
31
31
 
32
32
  ### TableSelectProps
33
33
 
34
- | Property | Type | Description | Default Value |
35
- | --- | --- | --- | --- |
36
- | prefixCls | `string` | Custom class name prefix | `'yee-table-select'` |
37
- | className | `string` | Root element class name | - |
38
- | style | `React.CSSProperties` | Root element inline styles | - |
39
- | rowKey | `string \| ((record: any) => string)` | Unique identifier field name for data | `'id'` |
40
- | columns | `Array<any>` | Table column configuration | - |
41
- | dataSource | `Array<InDataType>` | Data source | - |
42
- | rowSelection | `RowSelectionType` | Row selection configuration | - |
43
- | value | `SelectionKeyType \| SelectionKeyType[]` | Controlled value | - |
44
- | defaultValue | `SelectionKeyType \| SelectionKeyType[]` | Default value | - |
45
- | disabled | `boolean` | Whether disabled | - |
46
- | loading | `boolean` | Whether data is loading; displays a gray loading indicator | `false` |
47
- | placement | `'bottomLeft' \| 'topLeft'` | Popup placement | `'bottomLeft'` |
48
- | searchable | `boolean` | Whether searchable | `true` |
49
- | searchOnInput | `boolean` | Whether to search while typing | `true` |
50
- | allowClear | `boolean` | Whether allow clear | `true` |
51
- | closable | `boolean` | Whether tags are closable | `true` |
52
- | placeholder | `string` | Placeholder text | - |
53
- | optionLabelProp | `string \| ((option: InDataType) => string)` | Option label property | `'name'` |
54
- | type | `'radio' \| 'checkbox'` | Selection type | `'radio'` |
55
- | options | `TagType[]` | Preset options | - |
56
- | isShowFilter | `boolean` | Whether to show filter (All/Selected for multiple) | `true` |
57
- | containerId | `string` | Container ID (for popup mounting) | - |
58
- | io | `'out'` | Input direction | - |
59
- | onChange | `(keys, data, checked, changeData) => void` | Selection change callback | - |
60
- | onSearch | `(value: string, e?) => void` | Search callback | - |
61
- | onOpenChange | `(open: boolean) => void` | Popup state change callback | - |
34
+ | Property | Type | Description | Default Value |
35
+ | --------------- | ----------------------------------------------------- | ---------------------------------------------------------- | -------------------- |
36
+ | prefixCls | `string` | Custom class name prefix | `'yee-table-select'` |
37
+ | className | `string` | Root element class name | - |
38
+ | style | `React.CSSProperties` | Root element inline styles | - |
39
+ | rowKey | `string \| ((record: any) => string)` | Unique identifier field name for data | `'id'` |
40
+ | columns | `Array<any>` | Table column configuration | - |
41
+ | dataSource | `Array<TableSelectDataType>` | Data source | - |
42
+ | rowSelection | `RowSelectionType` | Row selection configuration | - |
43
+ | value | `SelectionKeyType \| SelectionKeyType[]` | Controlled value | - |
44
+ | defaultValue | `SelectionKeyType \| SelectionKeyType[]` | Default value | - |
45
+ | disabled | `boolean` | Whether disabled | - |
46
+ | loading | `boolean` | Whether data is loading; displays a gray loading indicator | `false` |
47
+ | placement | `'bottomLeft' \| 'topLeft'` | Popup placement | `'bottomLeft'` |
48
+ | searchable | `boolean` | Whether searchable | `true` |
49
+ | searchOnInput | `boolean` | Whether to search while typing | `true` |
50
+ | allowClear | `boolean` | Whether allow clear | `true` |
51
+ | closable | `boolean` | Whether tags are closable | `true` |
52
+ | placeholder | `string` | Placeholder text | - |
53
+ | optionLabelProp | `string \| ((option: TableSelectDataType) => string)` | Option label property | `'name'` |
54
+ | type | `'radio' \| 'checkbox'` | Selection type | `'radio'` |
55
+ | options | `TagType[]` | Preset options | - |
56
+ | isShowFilter | `boolean` | Whether to show filter (All/Selected for multiple) | `true` |
57
+ | containerId | `string` | Container ID (for popup mounting) | - |
58
+ | io | `'out'` | Input direction | - |
59
+ | onChange | `(keys, data, checked, changeData) => void` | Selection change callback | - |
60
+ | onSearch | `(value: string, e?) => void` | Search callback | - |
61
+ | onOpenChange | `(open: boolean) => void` | Popup state change callback | - |
62
62
 
63
63
  ### RowSelectionType
64
64
 
65
65
  Row selection configuration, inherited from Table component's RowSelectionType.
66
66
 
67
- | Property | Type | Description | Default Value |
68
- | --- | --- | --- | --- |
69
- | type | `'radio' \| 'checkbox'` | Single/multiple selection | - |
70
- | selectedRowKeys | `Array<SelectionKeyType> \| SelectionKeyType` | (Controlled) selected row keys | - |
71
- | defaultSelectedRowKeys | `Array<SelectionKeyType> \| SelectionKeyType` | Default selected row keys | - |
72
- | onChange | `(selectedRowKeys, selectedRows) => void` | Callback when selection changes | - |
67
+ | Property | Type | Description | Default Value |
68
+ | ---------------------- | --------------------------------------------- | ------------------------------- | ------------- |
69
+ | type | `'radio' \| 'checkbox'` | Single/multiple selection | - |
70
+ | selectedRowKeys | `Array<SelectionKeyType> \| SelectionKeyType` | (Controlled) selected row keys | - |
71
+ | defaultSelectedRowKeys | `Array<SelectionKeyType> \| SelectionKeyType` | Default selected row keys | - |
72
+ | onChange | `(selectedRowKeys, selectedRows) => void` | Callback when selection changes | - |
73
73
 
74
74
  ## Methods
75
75
 
@@ -77,17 +77,20 @@ Row selection configuration, inherited from Table component's RowSelectionType.
77
77
 
78
78
  InternalTable is an internal component of TableSelect and typically doesn't need to be used directly.
79
79
 
80
- | Property | Type | Description | Default Value |
81
- | --- | --- | --- | --- |
82
- | rowKey | `string` | Unique identifier field name for data | - |
83
- | columns | `Array<any>` | Table column configuration | - |
84
- | dataSource | `Array<InDataType>` | Data source | - |
85
- | rowSelection | `RowSelectionType` | Row selection configuration | - |
86
- | type | `'radio' \| 'checkbox'` | Selection type | - |
87
- | value | `SelectedKeyType \| Array<SelectedKeyType>` | Selected value | - |
88
- | isShowFilter | `boolean` | Whether to show filter | `true` |
89
- | visible | `boolean` | Whether popup is visible | - |
80
+ | Property | Type | Description | Default Value |
81
+ | ------------ | ------------------------------------------- | ---------------------------------------------------------------------- | ------------- |
82
+ | rowKey | `string` | Unique identifier field name for data | - |
83
+ | columns | `Array<any>` | Table column configuration | - |
84
+ | dataSource | `Array<TableSelectDataType>` | Data source | - |
85
+ | rowSelection | `RowSelectionType` | Row selection configuration | - |
86
+ | type | `'radio' \| 'checkbox'` | Selection type | - |
87
+ | value | `SelectedKeyType \| Array<SelectedKeyType>` | Selected value | - |
88
+ | isShowFilter | `boolean` | Whether to show filter | `true` |
89
+ | visible | `boolean` | Whether popup is visible | - |
90
+ | popupRef | `React.RefObject<HTMLDivElement \| null>` | Ref to the popup root element, used internally for keyboard navigation | - |
90
91
 
91
92
  ## Notes
92
93
 
94
+ - When the dropdown is open, keyboard is supported: `↑`/`↓` move the highlight across rows, `Space` selects/toggles the highlighted row, `Esc` closes the dropdown; the text caret always stays in the input; press `Enter` while closed to open the dropdown
95
+
93
96
  - Adapted for mobile (popup constrained with max-width/height and scrollable)
@@ -31,45 +31,45 @@ toc: 'content'
31
31
 
32
32
  ### TableSelectProps
33
33
 
34
- | 属性名 | 类型 | 描述 | 默认值 |
35
- | --- | --- | --- | --- |
36
- | prefixCls | `string` | 自定义类名前缀 | `'yee-table-select'` |
37
- | className | `string` | 根元素类名 | - |
38
- | style | `React.CSSProperties` | 根元素行内样式 | - |
39
- | rowKey | `string \| ((record: any) => string)` | 数据的唯一标识字段名 | `'id'` |
40
- | columns | `Array<any>` | 表格列的配置信息 | - |
41
- | dataSource | `Array<InDataType>` | 数据源 | - |
42
- | rowSelection | `RowSelectionType` | 行选择配置 | - |
43
- | value | `SelectionKeyType \| SelectionKeyType[]` | 受控值 | - |
44
- | defaultValue | `SelectionKeyType \| SelectionKeyType[]` | 默认值 | - |
45
- | disabled | `boolean` | 是否禁用 | - |
46
- | loading | `boolean` | 是否处于加载状态,加载时显示灰色加载图标 | `false` |
47
- | placement | `'bottomLeft' \| 'topLeft'` | 弹出层位置 | `'bottomLeft'` |
48
- | searchable | `boolean` | 是否可搜索 | `true` |
49
- | searchOnInput | `boolean` | 是否边输入边搜索 | `true` |
50
- | allowClear | `boolean` | 是否允许清除 | `true` |
51
- | closable | `boolean` | 标签是否可关闭 | `true` |
52
- | placeholder | `string` | 占位符文本 | - |
53
- | optionLabelProp | `string \| ((option: InDataType) => string)` | 选项标签属性 | `'name'` |
54
- | type | `'radio' \| 'checkbox'` | 选择类型 | `'radio'` |
55
- | options | `TagType[]` | 预设选项 | - |
56
- | isShowFilter | `boolean` | 是否显示过滤器(多选时 All/Selected) | `true` |
57
- | containerId | `string` | 容器 ID(用于弹出层挂载) | - |
58
- | io | `'out'` | 输入方向 | - |
59
- | onChange | `(keys, data, checked, changeData) => void` | 选中变更回调 | - |
60
- | onSearch | `(value: string, e?) => void` | 搜索回调 | - |
61
- | onOpenChange | `(open: boolean) => void` | 弹出层状态变更回调 | - |
34
+ | 属性名 | 类型 | 描述 | 默认值 |
35
+ | --------------- | ----------------------------------------------------- | ---------------------------------------- | -------------------- |
36
+ | prefixCls | `string` | 自定义类名前缀 | `'yee-table-select'` |
37
+ | className | `string` | 根元素类名 | - |
38
+ | style | `React.CSSProperties` | 根元素行内样式 | - |
39
+ | rowKey | `string \| ((record: any) => string)` | 数据的唯一标识字段名 | `'id'` |
40
+ | columns | `Array<any>` | 表格列的配置信息 | - |
41
+ | dataSource | `Array<TableSelectDataType>` | 数据源 | - |
42
+ | rowSelection | `RowSelectionType` | 行选择配置 | - |
43
+ | value | `SelectionKeyType \| SelectionKeyType[]` | 受控值 | - |
44
+ | defaultValue | `SelectionKeyType \| SelectionKeyType[]` | 默认值 | - |
45
+ | disabled | `boolean` | 是否禁用 | - |
46
+ | loading | `boolean` | 是否处于加载状态,加载时显示灰色加载图标 | `false` |
47
+ | placement | `'bottomLeft' \| 'topLeft'` | 弹出层位置 | `'bottomLeft'` |
48
+ | searchable | `boolean` | 是否可搜索 | `true` |
49
+ | searchOnInput | `boolean` | 是否边输入边搜索 | `true` |
50
+ | allowClear | `boolean` | 是否允许清除 | `true` |
51
+ | closable | `boolean` | 标签是否可关闭 | `true` |
52
+ | placeholder | `string` | 占位符文本 | - |
53
+ | optionLabelProp | `string \| ((option: TableSelectDataType) => string)` | 选项标签属性 | `'name'` |
54
+ | type | `'radio' \| 'checkbox'` | 选择类型 | `'radio'` |
55
+ | options | `TagType[]` | 预设选项 | - |
56
+ | isShowFilter | `boolean` | 是否显示过滤器(多选时 All/Selected) | `true` |
57
+ | containerId | `string` | 容器 ID(用于弹出层挂载) | - |
58
+ | io | `'out'` | 输入方向 | - |
59
+ | onChange | `(keys, data, checked, changeData) => void` | 选中变更回调 | - |
60
+ | onSearch | `(value: string, e?) => void` | 搜索回调 | - |
61
+ | onOpenChange | `(open: boolean) => void` | 弹出层状态变更回调 | - |
62
62
 
63
63
  ### RowSelectionType
64
64
 
65
65
  表格行选择配置,继承自 Table 组件的 RowSelectionType。
66
66
 
67
- | 属性名 | 类型 | 描述 | 默认值 |
68
- | --- | --- | --- | --- |
69
- | type | `'radio' \| 'checkbox'` | 单选/多选 | - |
70
- | selectedRowKeys | `Array<SelectionKeyType> \| SelectionKeyType` | (受控)选中的行keys | - |
71
- | defaultSelectedRowKeys | `Array<SelectionKeyType> \| SelectionKeyType` | 默认选中的行keys | - |
72
- | onChange | `(selectedRowKeys, selectedRows) => void` | 选中项发生变化时的回调 | - |
67
+ | 属性名 | 类型 | 描述 | 默认值 |
68
+ | ---------------------- | --------------------------------------------- | ---------------------- | ------ |
69
+ | type | `'radio' \| 'checkbox'` | 单选/多选 | - |
70
+ | selectedRowKeys | `Array<SelectionKeyType> \| SelectionKeyType` | (受控)选中的行keys | - |
71
+ | defaultSelectedRowKeys | `Array<SelectionKeyType> \| SelectionKeyType` | 默认选中的行keys | - |
72
+ | onChange | `(selectedRowKeys, selectedRows) => void` | 选中项发生变化时的回调 | - |
73
73
 
74
74
  ## 方法
75
75
 
@@ -77,17 +77,20 @@ toc: 'content'
77
77
 
78
78
  InternalTable 组件是 TableSelect 的内部组件,通常不需要直接使用。
79
79
 
80
- | 属性名 | 类型 | 描述 | 默认值 |
81
- | --- | --- | --- | --- |
82
- | rowKey | `string` | 数据的唯一标识字段名 | - |
83
- | columns | `Array<any>` | 表格列的配置信息 | - |
84
- | dataSource | `Array<InDataType>` | 数据源 | - |
85
- | rowSelection | `RowSelectionType` | 行选择配置 | - |
86
- | type | `'radio' \| 'checkbox'` | 选择类型 | - |
87
- | value | `SelectedKeyType \| Array<SelectedKeyType>` | 选中的值 | - |
88
- | isShowFilter | `boolean` | 是否显示过滤器 | `true` |
89
- | visible | `boolean` | 弹出层是否可见 | - |
80
+ | 属性名 | 类型 | 描述 | 默认值 |
81
+ | ------------ | ------------------------------------------- | ---------------------------------- | ------ |
82
+ | rowKey | `string` | 数据的唯一标识字段名 | - |
83
+ | columns | `Array<any>` | 表格列的配置信息 | - |
84
+ | dataSource | `Array<TableSelectDataType>` | 数据源 | - |
85
+ | rowSelection | `RowSelectionType` | 行选择配置 | - |
86
+ | type | `'radio' \| 'checkbox'` | 选择类型 | - |
87
+ | value | `SelectedKeyType \| Array<SelectedKeyType>` | 选中的值 | - |
88
+ | isShowFilter | `boolean` | 是否显示过滤器 | `true` |
89
+ | visible | `boolean` | 弹出层是否可见 | - |
90
+ | popupRef | `React.RefObject<HTMLDivElement \| null>` | 弹层根元素 ref,供键盘导航内部使用 | - |
90
91
 
91
92
  ## 注意事项
92
93
 
94
+ - 下拉展开后支持键盘操作:`↑`/`↓` 在行间移动高亮,`空格` 选中/取消勾选高亮行,`Esc` 关闭下拉;输入光标始终保留在输入框内;收起时按 `回车` 可展开下拉
95
+
93
96
  - 已适配移动端(弹窗限制最大宽高并支持滚动)
@@ -10,7 +10,7 @@ type SelectedKeyType = string | number;
10
10
  /**
11
11
  * Data item type
12
12
  */
13
- type InDataType = {
13
+ export type TableSelectDataType = {
14
14
  [propName: string]: any;
15
15
  };
16
16
 
@@ -42,7 +42,7 @@ export interface InternalTableProps {
42
42
  /**
43
43
  * Data source
44
44
  */
45
- dataSource: Array<InDataType>;
45
+ dataSource: Array<TableSelectDataType>;
46
46
  /**
47
47
  * Custom class name
48
48
  */
@@ -67,6 +67,11 @@ export interface InternalTableProps {
67
67
  * Whether popup is visible (for focus trap)
68
68
  */
69
69
  visible?: boolean;
70
+ /**
71
+ * Ref to the popup root element, used by TableSelect for keyboard navigation
72
+ * @internal
73
+ */
74
+ popupRef?: React.RefObject<HTMLDivElement | null>;
70
75
  }
71
76
 
72
77
  /**
@@ -96,7 +101,7 @@ export interface TableSelectProps
96
101
  /**
97
102
  * Data source
98
103
  */
99
- dataSource: Array<InDataType>;
104
+ dataSource: Array<TableSelectDataType>;
100
105
  /**
101
106
  * Single or multiple selection
102
107
  * @default: 'radio'
@@ -180,7 +185,7 @@ export interface TableSelectProps
180
185
  * Option label property (text field for displaying labels)
181
186
  * @default 'name'
182
187
  */
183
- optionLabelProp?: string | ((option: InDataType) => string);
188
+ optionLabelProp?: string | ((option: TableSelectDataType) => string);
184
189
  /**
185
190
  * Selection change callback
186
191
  * @param keys Selected keys
@@ -7,7 +7,7 @@ import { InternalTableProps } from './interface';
7
7
 
8
8
  type SelectedKeyType = string | number;
9
9
 
10
- type InDataType = {
10
+ type TableSelectDataType = {
11
11
  [propName: string]: any;
12
12
  };
13
13
 
@@ -22,12 +22,13 @@ const InternalTable: React.FC<InternalTableProps> = (props) => {
22
22
  isShowFilter = true,
23
23
  rowSelection = {},
24
24
  visible,
25
+ popupRef: externalPopupRef,
25
26
  ...rest
26
27
  } = props;
27
28
 
28
- const [internalDataSource, setInternalDataSource] = useState<InDataType[]>(
29
- [],
30
- );
29
+ const [internalDataSource, setInternalDataSource] = useState<
30
+ TableSelectDataType[]
31
+ >([]);
31
32
  const renderRowSelection = { type, selectedRowKeys: value, ...rowSelection };
32
33
 
33
34
  useEffect(() => {
@@ -45,7 +46,7 @@ const InternalTable: React.FC<InternalTableProps> = (props) => {
45
46
  setInternalDataSource(dataSource);
46
47
  } else {
47
48
  setInternalDataSource(
48
- dataSource.filter((data: InDataType) =>
49
+ dataSource.filter((data: TableSelectDataType) =>
49
50
  (selectedRowKeys as SelectedKeyType[])?.includes(
50
51
  typeof rowKey === 'function' ? rowKey(data) : data[rowKey],
51
52
  ),
@@ -76,7 +77,14 @@ const InternalTable: React.FC<InternalTableProps> = (props) => {
76
77
  return null;
77
78
  };
78
79
  return (
79
- <div className={`${prefixCls}-popup`} tabIndex={0} ref={popupRef}>
80
+ <div
81
+ className={`${prefixCls}-popup`}
82
+ tabIndex={0}
83
+ ref={(node) => {
84
+ popupRef.current = node;
85
+ if (externalPopupRef) externalPopupRef.current = node;
86
+ }}
87
+ >
80
88
  {renderRadio()}
81
89
  <Table
82
90
  {...rest}
@@ -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
  }