@douyinfe/semi-foundation 2.97.0 → 2.98.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 (171) hide show
  1. package/codeHighlight/codeHighlight.scss +1 -1
  2. package/datePicker/foundation.ts +7 -0
  3. package/datePicker/inputFoundation.ts +5 -0
  4. package/form/foundation.ts +48 -4
  5. package/inputNumber/foundation.ts +119 -3
  6. package/jsonViewer/jsonViewer.scss +2 -2
  7. package/lib/cjs/aiChatInput/aiChatInput.css +7 -7
  8. package/lib/cjs/anchor/anchor.css +3 -3
  9. package/lib/cjs/autoComplete/autoComplete.css +1 -1
  10. package/lib/cjs/avatar/avatar.css +5 -5
  11. package/lib/cjs/badge/badge.css +1 -1
  12. package/lib/cjs/breadcrumb/breadcrumb.css +2 -2
  13. package/lib/cjs/calendar/calendar.css +9 -9
  14. package/lib/cjs/cascader/cascader.css +6 -6
  15. package/lib/cjs/checkbox/checkbox.css +2 -2
  16. package/lib/cjs/codeHighlight/codeHighlight.css +1 -1
  17. package/lib/cjs/codeHighlight/codeHighlight.scss +1 -1
  18. package/lib/cjs/collapse/collapse.css +2 -2
  19. package/lib/cjs/datePicker/datePicker.css +8 -8
  20. package/lib/cjs/datePicker/foundation.d.ts +5 -0
  21. package/lib/cjs/datePicker/foundation.js +2 -0
  22. package/lib/cjs/datePicker/inputFoundation.d.ts +5 -0
  23. package/lib/cjs/descriptions/descriptions.css +6 -6
  24. package/lib/cjs/dropdown/dropdown.css +2 -2
  25. package/lib/cjs/form/form.css +4 -4
  26. package/lib/cjs/form/foundation.js +49 -3
  27. package/lib/cjs/hotKeys/hotKeys.css +2 -2
  28. package/lib/cjs/image/image.css +2 -2
  29. package/lib/cjs/input/input.css +8 -8
  30. package/lib/cjs/input/textarea.css +2 -2
  31. package/lib/cjs/inputNumber/foundation.d.ts +15 -0
  32. package/lib/cjs/inputNumber/foundation.js +113 -3
  33. package/lib/cjs/jsonViewer/jsonViewer.css +2 -2
  34. package/lib/cjs/jsonViewer/jsonViewer.scss +2 -2
  35. package/lib/cjs/list/list.css +1 -1
  36. package/lib/cjs/modal/modal.css +1 -1
  37. package/lib/cjs/navigation/navigation.css +2 -2
  38. package/lib/cjs/notification/notification.css +4 -4
  39. package/lib/cjs/pagination/pagination.css +5 -5
  40. package/lib/cjs/popconfirm/popconfirm.css +1 -1
  41. package/lib/cjs/popover/popover.css +1 -1
  42. package/lib/cjs/radio/radio.css +2 -2
  43. package/lib/cjs/scrollList/itemFoundation.js +12 -0
  44. package/lib/cjs/scrollList/scrollList.css +2 -2
  45. package/lib/cjs/select/select.css +6 -6
  46. package/lib/cjs/sideSheet/sideSheet.css +2 -2
  47. package/lib/cjs/sidebar/sidebar.css +11 -11
  48. package/lib/cjs/slider/foundation.js +46 -12
  49. package/lib/cjs/slider/rtl.scss +62 -0
  50. package/lib/cjs/slider/slider.css +45 -0
  51. package/lib/cjs/slider/slider.scss +2 -0
  52. package/lib/cjs/steps/steps.css +11 -11
  53. package/lib/cjs/table/foundation.d.ts +36 -0
  54. package/lib/cjs/table/foundation.js +162 -28
  55. package/lib/cjs/table/table.css +10 -2
  56. package/lib/cjs/table/table.scss +17 -0
  57. package/lib/cjs/tabs/tabs.css +2 -2
  58. package/lib/cjs/tag/tag.css +2 -2
  59. package/lib/cjs/tagInput/tagInput.css +2 -2
  60. package/lib/cjs/timePicker/timePicker.css +1 -1
  61. package/lib/cjs/timeline/timeline.css +2 -2
  62. package/lib/cjs/toast/toast.css +1 -1
  63. package/lib/cjs/tooltip/foundation.js +8 -5
  64. package/lib/cjs/tooltip/tooltip.css +1 -1
  65. package/lib/cjs/transfer/constants.d.ts +3 -1
  66. package/lib/cjs/transfer/constants.js +3 -1
  67. package/lib/cjs/transfer/foundation.d.ts +3 -0
  68. package/lib/cjs/transfer/foundation.js +4 -0
  69. package/lib/cjs/transfer/transfer.css +14 -5
  70. package/lib/cjs/transfer/transfer.scss +10 -0
  71. package/lib/cjs/tree/foundation.d.ts +3 -0
  72. package/lib/cjs/tree/foundation.js +31 -4
  73. package/lib/cjs/tree/tree.css +1 -1
  74. package/lib/cjs/treeSelect/foundation.d.ts +1 -0
  75. package/lib/cjs/treeSelect/foundation.js +8 -1
  76. package/lib/cjs/treeSelect/treeSelect.css +36 -4
  77. package/lib/cjs/treeSelect/treeSelect.scss +49 -1
  78. package/lib/cjs/typography/typography.css +8 -8
  79. package/lib/cjs/upload/upload.css +8 -8
  80. package/lib/cjs/utils/Store.d.ts +1 -1
  81. package/lib/cjs/utils/Store.js +1 -0
  82. package/lib/es/aiChatInput/aiChatInput.css +7 -7
  83. package/lib/es/anchor/anchor.css +3 -3
  84. package/lib/es/autoComplete/autoComplete.css +1 -1
  85. package/lib/es/avatar/avatar.css +5 -5
  86. package/lib/es/badge/badge.css +1 -1
  87. package/lib/es/breadcrumb/breadcrumb.css +2 -2
  88. package/lib/es/calendar/calendar.css +9 -9
  89. package/lib/es/cascader/cascader.css +6 -6
  90. package/lib/es/checkbox/checkbox.css +2 -2
  91. package/lib/es/codeHighlight/codeHighlight.css +1 -1
  92. package/lib/es/codeHighlight/codeHighlight.scss +1 -1
  93. package/lib/es/collapse/collapse.css +2 -2
  94. package/lib/es/datePicker/datePicker.css +8 -8
  95. package/lib/es/datePicker/foundation.d.ts +5 -0
  96. package/lib/es/datePicker/foundation.js +2 -0
  97. package/lib/es/datePicker/inputFoundation.d.ts +5 -0
  98. package/lib/es/descriptions/descriptions.css +6 -6
  99. package/lib/es/dropdown/dropdown.css +2 -2
  100. package/lib/es/form/form.css +4 -4
  101. package/lib/es/form/foundation.js +49 -3
  102. package/lib/es/hotKeys/hotKeys.css +2 -2
  103. package/lib/es/image/image.css +2 -2
  104. package/lib/es/input/input.css +8 -8
  105. package/lib/es/input/textarea.css +2 -2
  106. package/lib/es/inputNumber/foundation.d.ts +15 -0
  107. package/lib/es/inputNumber/foundation.js +113 -3
  108. package/lib/es/jsonViewer/jsonViewer.css +2 -2
  109. package/lib/es/jsonViewer/jsonViewer.scss +2 -2
  110. package/lib/es/list/list.css +1 -1
  111. package/lib/es/modal/modal.css +1 -1
  112. package/lib/es/navigation/navigation.css +2 -2
  113. package/lib/es/notification/notification.css +4 -4
  114. package/lib/es/pagination/pagination.css +5 -5
  115. package/lib/es/popconfirm/popconfirm.css +1 -1
  116. package/lib/es/popover/popover.css +1 -1
  117. package/lib/es/radio/radio.css +2 -2
  118. package/lib/es/scrollList/itemFoundation.js +12 -0
  119. package/lib/es/scrollList/scrollList.css +2 -2
  120. package/lib/es/select/select.css +6 -6
  121. package/lib/es/sideSheet/sideSheet.css +2 -2
  122. package/lib/es/sidebar/sidebar.css +11 -11
  123. package/lib/es/slider/foundation.js +46 -12
  124. package/lib/es/slider/rtl.scss +62 -0
  125. package/lib/es/slider/slider.css +45 -0
  126. package/lib/es/slider/slider.scss +2 -0
  127. package/lib/es/steps/steps.css +11 -11
  128. package/lib/es/table/foundation.d.ts +36 -0
  129. package/lib/es/table/foundation.js +162 -28
  130. package/lib/es/table/table.css +10 -2
  131. package/lib/es/table/table.scss +17 -0
  132. package/lib/es/tabs/tabs.css +2 -2
  133. package/lib/es/tag/tag.css +2 -2
  134. package/lib/es/tagInput/tagInput.css +2 -2
  135. package/lib/es/timePicker/timePicker.css +1 -1
  136. package/lib/es/timeline/timeline.css +2 -2
  137. package/lib/es/toast/toast.css +1 -1
  138. package/lib/es/tooltip/foundation.js +8 -5
  139. package/lib/es/tooltip/tooltip.css +1 -1
  140. package/lib/es/transfer/constants.d.ts +3 -1
  141. package/lib/es/transfer/constants.js +3 -1
  142. package/lib/es/transfer/foundation.d.ts +3 -0
  143. package/lib/es/transfer/foundation.js +4 -0
  144. package/lib/es/transfer/transfer.css +14 -5
  145. package/lib/es/transfer/transfer.scss +10 -0
  146. package/lib/es/tree/foundation.d.ts +3 -0
  147. package/lib/es/tree/foundation.js +31 -4
  148. package/lib/es/tree/tree.css +1 -1
  149. package/lib/es/treeSelect/foundation.d.ts +1 -0
  150. package/lib/es/treeSelect/foundation.js +8 -1
  151. package/lib/es/treeSelect/treeSelect.css +36 -4
  152. package/lib/es/treeSelect/treeSelect.scss +49 -1
  153. package/lib/es/typography/typography.css +8 -8
  154. package/lib/es/upload/upload.css +8 -8
  155. package/lib/es/utils/Store.d.ts +1 -1
  156. package/lib/es/utils/Store.js +1 -0
  157. package/package.json +19 -4
  158. package/scrollList/itemFoundation.ts +12 -0
  159. package/slider/foundation.ts +55 -15
  160. package/slider/rtl.scss +62 -0
  161. package/slider/slider.scss +2 -0
  162. package/table/foundation.ts +197 -29
  163. package/table/table.scss +17 -0
  164. package/tooltip/foundation.ts +8 -5
  165. package/transfer/constants.ts +3 -1
  166. package/transfer/foundation.ts +8 -1
  167. package/transfer/transfer.scss +10 -0
  168. package/tree/foundation.ts +34 -5
  169. package/treeSelect/foundation.ts +10 -1
  170. package/treeSelect/treeSelect.scss +49 -1
  171. package/utils/Store.ts +2 -1
@@ -0,0 +1,62 @@
1
+ $module: #{$prefix}-slider;
2
+
3
+ // RTL support (horizontal)
4
+ // Slider uses `left` positioning in LTR. In RTL we switch to `right` positioning
5
+ // in the component logic, so centering transforms must be mirrored.
6
+ .#{$prefix}-rtl,
7
+ .#{$prefix}-portal-rtl {
8
+ .#{$module} {
9
+ direction: rtl;
10
+
11
+ &-handle {
12
+ transform: $transform_scale-slider_handle translateX(50%) translateY($spacing-slider_handle-translateY);
13
+ }
14
+
15
+ &-mark {
16
+ transform: translate(50%, 0);
17
+ }
18
+
19
+ &-mark-reverse {
20
+ transform: translate(50%, 0) rotate(-180deg);
21
+ }
22
+
23
+ // Boundary text should match RTL direction: min on the right, max on the left
24
+
25
+ &-boundary-min {
26
+ left: auto;
27
+ right: $spacing-slider_boundary_min-left;
28
+ }
29
+
30
+ &-boundary-max {
31
+ right: auto;
32
+ left: $spacing-slider_boundary_max-right;
33
+ }
34
+ }
35
+ }
36
+
37
+ // Backward/explicit support: the component may add `semi-slider-rtl` on wrapper.
38
+ // Keep these rules so RTL transforms still apply even without `.semi-rtl` container.
39
+
40
+ .#{$module}-rtl {
41
+ .#{$module}-handle {
42
+ transform: $transform_scale-slider_handle translateX(50%) translateY($spacing-slider_handle-translateY);
43
+ }
44
+
45
+ .#{$module}-mark {
46
+ transform: translate(50%, 0);
47
+ }
48
+
49
+ .#{$module}-mark-reverse {
50
+ transform: translate(50%, 0) rotate(-180deg);
51
+ }
52
+
53
+ .#{$module}-boundary-min {
54
+ left: auto;
55
+ right: $spacing-slider_boundary_min-left;
56
+ }
57
+
58
+ .#{$module}-boundary-max {
59
+ right: auto;
60
+ left: $spacing-slider_boundary_max-right;
61
+ }
62
+ }
@@ -252,3 +252,5 @@ $module: #{$prefix}-slider;
252
252
  .#{$module}-reverse {
253
253
  transform: rotate(180deg);
254
254
  }
255
+
256
+ @import "./rtl.scss";
@@ -21,6 +21,13 @@ import BaseFoundation, { DefaultAdapter } from '../base/foundation';
21
21
  import { strings, numbers } from './constants';
22
22
  import { mergeQueries, flattenColumns, filterColumns } from './utils';
23
23
  import { pullAll, withOrderSort } from '../utils/array';
24
+ import {
25
+ convertDataToEntities,
26
+ calcCheckedKeys,
27
+ calcCheckedKeysForChecked,
28
+ calcCheckedKeysForUnchecked,
29
+ findDescendantKeys,
30
+ } from '../tree/treeUtil';
24
31
 
25
32
  export interface BaseColumnProps<RecordType> {
26
33
  align?: BaseAlign;
@@ -78,8 +85,11 @@ export interface TableAdapter<RecordType> extends DefaultAdapter {
78
85
  setFlattenData: (flattenData: RecordType[]) => void;
79
86
  setAllRowKeys: (allRowKeys: BaseRowKeyType[]) => void;
80
87
  setHoveredRowKey: (hoveredRowKey: BaseRowKeyType) => void;
88
+ setHoveredRowKeys: (hoveredRowKeys: BaseRowKeyType[]) => void;
81
89
  setCachedFilteredSortedDataSource: (filteredSortedDataSource: RecordType[]) => void;
82
90
  setCachedFilteredSortedRowKeys: (filteredSortedRowKeys: BaseRowKeyType[]) => void;
91
+ setHalfCheckedRowKeys: (halfCheckedRowKeys: BaseRowKeyType[]) => void;
92
+ setKeyEntities: (keyEntities: BaseEntitys) => void;
83
93
  getCurrentPage: () => number;
84
94
  getCurrentPageSize: () => number;
85
95
  getCachedFilteredSortedDataSource: () => RecordType[];
@@ -88,6 +98,9 @@ export interface TableAdapter<RecordType> extends DefaultAdapter {
88
98
  setAllDisabledRowKeys: (allDisabledRowKeys: BaseRowKeyType[]) => void;
89
99
  getAllDisabledRowKeys: () => BaseRowKeyType[];
90
100
  getAllDisabledRowKeysSet: () => Set<BaseRowKeyType>;
101
+ getHalfCheckedRowKeys: () => BaseRowKeyType[];
102
+ getHalfCheckedRowKeysSet: () => Set<BaseRowKeyType>;
103
+ getKeyEntities: () => BaseEntitys;
91
104
  notifyFilterDropdownVisibleChange: (visible: boolean, dataIndex: string) => void;
92
105
  notifyChange: (changeInfo: { pagination: BasePagination; filters: BaseChangeInfoFilter<RecordType>[]; sorter: BaseChangeInfoSorter<RecordType>; extra: OnChangeExtra }) => void;
93
106
  notifyExpand: (expanded?: boolean, record?: BaseIncludeGroupRecord<RecordType>, mouseEvent?: any) => void;
@@ -107,7 +120,8 @@ export interface TableAdapter<RecordType> extends DefaultAdapter {
107
120
  getHandleColumns: () => (queries: BaseColumnProps<RecordType>[], cachedColumns: BaseColumnProps<RecordType>[]) => BaseColumnProps<RecordType>[];
108
121
  getMergePagination: () => (pagination: BasePagination) => BasePagination;
109
122
  setBodyHasScrollbar: (bodyHasScrollBar: boolean) => void;
110
- getTableLayout: () => 'fixed' | 'auto'
123
+ getTableLayout: () => 'fixed' | 'auto';
124
+ getCheckRelation: () => CheckRelation
111
125
  }
112
126
 
113
127
  class TableFoundation<RecordType> extends BaseFoundation<TableAdapter<RecordType>> {
@@ -756,30 +770,118 @@ class TableFoundation<RecordType> extends BaseFoundation<TableAdapter<RecordType
756
770
  return !(Array.isArray(dataSource) && dataSource.length > 0);
757
771
  }
758
772
 
773
+ /**
774
+ * Build tree data entities for checkRelation
775
+ * @param dataSource
776
+ * @returns keyEntities map
777
+ */
778
+ buildKeyEntities(dataSource?: RecordType[]): BaseEntitys {
779
+ dataSource = dataSource == null ? this._getDataSource() : dataSource;
780
+ const childrenRecordName = this.getProp('childrenRecordName');
781
+ const rowKey = this.getProp('rowKey');
782
+
783
+ // Convert table data to tree data format
784
+ const convertToTreeData = (data: RecordType[]): any[] => {
785
+ return data.map(record => {
786
+ const key = typeof rowKey === 'function' ? rowKey(record) : get(record, rowKey);
787
+ const children = get(record, childrenRecordName);
788
+ const node: any = { key, ...record };
789
+ if (Array.isArray(children) && children.length) {
790
+ node.children = convertToTreeData(children);
791
+ }
792
+ return node;
793
+ });
794
+ };
795
+
796
+ const treeData = convertToTreeData(dataSource);
797
+ const { keyEntities } = convertDataToEntities(treeData, { key: 'key', children: 'children' });
798
+ return keyEntities;
799
+ }
800
+
801
+ /**
802
+ * Calculate checked keys when checkRelation is 'related'
803
+ * @param realKey
804
+ * @param selected
805
+ * @param checkedKeys
806
+ * @param halfCheckedKeys
807
+ */
808
+ calcCheckedKeysForSelect(realKey: BaseRowKeyType, selected: boolean, checkedKeys: Set<string>, halfCheckedKeys: Set<string>) {
809
+ const keyEntities = this._adapter.getKeyEntities();
810
+ const keyStr = String(realKey);
811
+
812
+ // If keyEntities doesn't contain this key, handle it as a simple add/remove
813
+ if (!keyEntities || !keyEntities[keyStr]) {
814
+ if (selected) {
815
+ checkedKeys.add(keyStr);
816
+ } else {
817
+ checkedKeys.delete(keyStr);
818
+ }
819
+ return { checkedKeys, halfCheckedKeys };
820
+ }
821
+
822
+ if (selected) {
823
+ return calcCheckedKeysForChecked(keyStr, keyEntities, checkedKeys, halfCheckedKeys);
824
+ } else {
825
+ return calcCheckedKeysForUnchecked(keyStr, keyEntities, checkedKeys, halfCheckedKeys);
826
+ }
827
+ }
828
+
759
829
  handleSelectRow(realKey: BaseRowKeyType, selected: boolean, e: any) {
760
830
  this.stopPropagation(e);
761
831
  if (typeof selected === 'boolean' && realKey != null) {
832
+ const checkRelation = this._adapter.getCheckRelation();
762
833
  const selectedRowKeys = this._getSelectedRowKeys();
834
+ const halfCheckedRowKeys = [...(this._adapter.getHalfCheckedRowKeys() || [])];
763
835
  let foundIdx = -1;
764
836
  const selectedRow = this.getSelectedRows(null, [realKey])[0];
765
837
  let selectedRows: BaseIncludeGroupRecord<RecordType>[];
766
838
 
767
- if ((foundIdx = selectedRowKeys.indexOf(realKey)) > -1 && selected === false) {
768
- selectedRowKeys.splice(foundIdx, 1);
769
- selectedRows = this.getSelectedRows(null, selectedRowKeys);
839
+ if (checkRelation === 'related') {
840
+ // When checkRelation is 'related', use tree selection logic
841
+ const keyEntities = this._adapter.getKeyEntities();
842
+ // Convert keys to strings for tree utility functions
843
+ const checkedKeysSet = new Set(selectedRowKeys.map(key => String(key)));
844
+ const halfCheckedKeysSet = new Set(halfCheckedRowKeys.map(key => String(key)));
845
+
846
+ const { checkedKeys, halfCheckedKeys } = this.calcCheckedKeysForSelect(
847
+ String(realKey),
848
+ selected,
849
+ checkedKeysSet,
850
+ halfCheckedKeysSet
851
+ );
852
+
853
+ const newSelectedRowKeys = [...checkedKeys];
854
+ const newHalfCheckedRowKeys = [...halfCheckedKeys];
855
+ selectedRows = this.getSelectedRows(null, newSelectedRowKeys);
856
+
857
+ // Always update halfCheckedRowKeys state for checkRelation='related' mode
858
+ // This is needed for rendering the half-checked state in the UI
859
+ this._adapter.setHalfCheckedRowKeys(newHalfCheckedRowKeys);
860
+
770
861
  if (!this._selectionIsControlled()) {
771
- this._adapter.setSelectedRowKeys(selectedRowKeys);
862
+ this._adapter.setSelectedRowKeys(newSelectedRowKeys);
772
863
  }
773
864
  this._adapter.notifySelect(selectedRow, selected, selectedRows, e);
774
- this._adapter.notifySelectionChange(selectedRowKeys, selectedRows);
775
- } else if (selectedRowKeys.indexOf(realKey) === -1 && selected === true) {
776
- selectedRowKeys.push(realKey);
777
- selectedRows = this.getSelectedRows(null, selectedRowKeys);
778
- if (!this._selectionIsControlled()) {
779
- this._adapter.setSelectedRowKeys(selectedRowKeys);
865
+ this._adapter.notifySelectionChange(newSelectedRowKeys, selectedRows);
866
+ } else {
867
+ // Original logic for unRelated mode
868
+ if ((foundIdx = selectedRowKeys.indexOf(realKey)) > -1 && selected === false) {
869
+ selectedRowKeys.splice(foundIdx, 1);
870
+ selectedRows = this.getSelectedRows(null, selectedRowKeys);
871
+ if (!this._selectionIsControlled()) {
872
+ this._adapter.setSelectedRowKeys(selectedRowKeys);
873
+ }
874
+ this._adapter.notifySelect(selectedRow, selected, selectedRows, e);
875
+ this._adapter.notifySelectionChange(selectedRowKeys, selectedRows);
876
+ } else if (selectedRowKeys.indexOf(realKey) === -1 && selected === true) {
877
+ selectedRowKeys.push(realKey);
878
+ selectedRows = this.getSelectedRows(null, selectedRowKeys);
879
+ if (!this._selectionIsControlled()) {
880
+ this._adapter.setSelectedRowKeys(selectedRowKeys);
881
+ }
882
+ this._adapter.notifySelect(selectedRow, selected, selectedRows, e);
883
+ this._adapter.notifySelectionChange(selectedRowKeys, selectedRows);
780
884
  }
781
- this._adapter.notifySelect(selectedRow, selected, selectedRows, e);
782
- this._adapter.notifySelectionChange(selectedRowKeys, selectedRows);
783
885
  }
784
886
  }
785
887
  }
@@ -792,6 +894,7 @@ class TableFoundation<RecordType> extends BaseFoundation<TableAdapter<RecordType
792
894
  handleSelectAllRow(selected: boolean, e: any) {
793
895
  this.stopPropagation(e);
794
896
  if (typeof selected === 'boolean') {
897
+ const checkRelation = this._adapter.getCheckRelation();
795
898
  const curSelectedRowKeys = this._getSelectedRowKeys();
796
899
  let selectedRowKeys = [...curSelectedRowKeys];
797
900
  const selectedRowKeysSet = this._getSelectedRowKeysSet();
@@ -800,28 +903,78 @@ class TableFoundation<RecordType> extends BaseFoundation<TableAdapter<RecordType
800
903
  const disabledRowKeysSet = this._adapter.getAllDisabledRowKeysSet();
801
904
  let changedRowKeys;
802
905
 
803
- // Select all, if not disabled && not in selectedRowKeys
804
- if (selected) {
805
- for (const key of allRowKeys) {
806
- if (!disabledRowKeysSet.has(key) && !selectedRowKeysSet.has(key)) {
807
- selectedRowKeys.push(key);
906
+ if (checkRelation === 'related') {
907
+ // When checkRelation is 'related', use tree selection logic
908
+ const keyEntities = this._adapter.getKeyEntities();
909
+ const halfCheckedRowKeys = [...(this._adapter.getHalfCheckedRowKeys() || [])];
910
+ // Convert keys to strings for tree utility functions
911
+ let checkedKeysSet = new Set(selectedRowKeys.map(key => String(key)));
912
+ let halfCheckedKeysSet = new Set(halfCheckedRowKeys.map(key => String(key)));
913
+
914
+ if (selected) {
915
+ // Select all: add all non-disabled keys
916
+ const keysToAdd = allRowKeys.filter(key => !disabledRowKeysSet.has(key));
917
+ for (const key of keysToAdd) {
918
+ const keyStr = String(key);
919
+ if (!checkedKeysSet.has(keyStr) && keyEntities && keyEntities[keyStr]) {
920
+ const result = calcCheckedKeysForChecked(keyStr, keyEntities, checkedKeysSet, halfCheckedKeysSet);
921
+ checkedKeysSet = result.checkedKeys;
922
+ halfCheckedKeysSet = result.halfCheckedKeys;
923
+ }
808
924
  }
925
+ changedRowKeys = keysToAdd;
926
+ } else {
927
+ // Deselect all: remove all keys
928
+ const keysToRemove = [...checkedKeysSet];
929
+ for (const key of keysToRemove) {
930
+ if (keyEntities && keyEntities[key]) {
931
+ const result = calcCheckedKeysForUnchecked(key, keyEntities, checkedKeysSet, halfCheckedKeysSet);
932
+ checkedKeysSet = result.checkedKeys;
933
+ halfCheckedKeysSet = result.halfCheckedKeys;
934
+ }
935
+ }
936
+ changedRowKeys = [...curSelectedRowKeys];
809
937
  }
810
- allRowKeys = pullAll(allRowKeys, [...disabledRowKeys, ...curSelectedRowKeys]);
811
- changedRowKeys = [...allRowKeys];
938
+
939
+ selectedRowKeys = [...checkedKeysSet];
940
+ const newHalfCheckedRowKeys = [...halfCheckedKeysSet];
941
+ const changedRows = this.getSelectedRows(null, changedRowKeys || []);
942
+ const selectedRows = this.getSelectedRows(null, selectedRowKeys || []);
943
+
944
+ // Always update halfCheckedRowKeys state for checkRelation='related' mode
945
+ // This is needed for rendering the half-checked state in the UI
946
+ this._adapter.setHalfCheckedRowKeys(newHalfCheckedRowKeys);
947
+
948
+ if (!this._selectionIsControlled()) {
949
+ this._adapter.setSelectedRowKeys(selectedRowKeys);
950
+ }
951
+ this._adapter.notifySelectAll(selected, selectedRows, changedRows, e);
952
+ this._adapter.notifySelectionChange(selectedRowKeys, selectedRows);
812
953
  } else {
813
- selectedRowKeys = pullAll(selectedRowKeys, allRowKeys);
814
- changedRowKeys = [...curSelectedRowKeys];
815
- }
954
+ // Original logic for unRelated mode
955
+ // Select all, if not disabled && not in selectedRowKeys
956
+ if (selected) {
957
+ for (const key of allRowKeys) {
958
+ if (!disabledRowKeysSet.has(key) && !selectedRowKeysSet.has(key)) {
959
+ selectedRowKeys.push(key);
960
+ }
961
+ }
962
+ allRowKeys = pullAll(allRowKeys, [...disabledRowKeys, ...curSelectedRowKeys]);
963
+ changedRowKeys = [...allRowKeys];
964
+ } else {
965
+ selectedRowKeys = pullAll(selectedRowKeys, allRowKeys);
966
+ changedRowKeys = [...curSelectedRowKeys];
967
+ }
816
968
 
817
- const changedRows = this.getSelectedRows(null, changedRowKeys || []);
818
- const selectedRows = this.getSelectedRows(null, selectedRowKeys || []);
969
+ const changedRows = this.getSelectedRows(null, changedRowKeys || []);
970
+ const selectedRows = this.getSelectedRows(null, selectedRowKeys || []);
819
971
 
820
- if (!this._selectionIsControlled()) {
821
- this._adapter.setSelectedRowKeys(selectedRowKeys);
972
+ if (!this._selectionIsControlled()) {
973
+ this._adapter.setSelectedRowKeys(selectedRowKeys);
974
+ }
975
+ this._adapter.notifySelectAll(selected, selectedRows, changedRows, e);
976
+ this._adapter.notifySelectionChange(selectedRowKeys, selectedRows);
822
977
  }
823
- this._adapter.notifySelectAll(selected, selectedRows, changedRows, e);
824
- this._adapter.notifySelectionChange(selectedRowKeys, selectedRows);
825
978
  }
826
979
  }
827
980
 
@@ -1283,4 +1436,19 @@ export type BaseIncludeGroupRecord<RecordType> = RecordType | { groupKey: string
1283
1436
 
1284
1437
  export type BaseEllipsis = boolean | { showTitle: boolean };
1285
1438
 
1439
+ export type CheckRelation = 'related' | 'unRelated';
1440
+
1441
+ export interface BaseEntity {
1442
+ key?: string | number;
1443
+ level?: number;
1444
+ children?: BaseEntity[];
1445
+ parent?: BaseEntity | null;
1446
+ data?: Record<string, any>;
1447
+ [key: string]: any
1448
+ }
1449
+
1450
+ export interface BaseEntitys {
1451
+ [key: string]: BaseEntity
1452
+ }
1453
+
1286
1454
  export default TableFoundation;
package/table/table.scss CHANGED
@@ -259,6 +259,23 @@ $module: #{$prefix}-table;
259
259
  display: table-row;
260
260
  background-color: $color-table_body-bg-default;
261
261
 
262
+ // Programmatic hover state (e.g. rowSpanHover feature)
263
+ // Keep visual effect consistent with native :hover
264
+ &.#{$module}-row-hovered {
265
+ & > .#{$module}-row-cell {
266
+ background-image: linear-gradient(0deg, $color-table_body-bg-hover, $color-table_body-bg-hover);
267
+ background-color: $color-table_cell-bg-hover;
268
+
269
+ &.#{$module}-cell-fixed {
270
+ &-left,
271
+ &-right {
272
+ background-image: linear-gradient(0deg, $color-table_body-bg-hover, $color-table_body-bg-hover);
273
+ background-color: $color-table_body-bg-default;
274
+ }
275
+ }
276
+ }
277
+ }
278
+
262
279
  &:hover {
263
280
  & > .#{$module}-row-cell {
264
281
  // $color-table_body-bg-hover has transparency,will reveal the background color $color-table_body-bg-default\
@@ -210,7 +210,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
210
210
  switch (types) {
211
211
  case 'focus':
212
212
  triggerEventSet[eventNames.focus] = () => {
213
- this.delayShow();
213
+ this.getProp('condition') !== false && this.delayShow();
214
214
  };
215
215
  triggerEventSet[eventNames.blur] = () => {
216
216
  this.delayHide();
@@ -220,7 +220,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
220
220
  case 'click':
221
221
  triggerEventSet[eventNames.click] = () => {
222
222
  // this.delayShow();
223
- this.show();
223
+ this.getProp('condition') !== false && this.show();
224
224
  };
225
225
  portalEventSet = {};
226
226
  // Click outside needs special treatment, can not be directly tied to the trigger Element, need to be bound to the document
@@ -229,7 +229,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
229
229
  triggerEventSet[eventNames.mouseEnter] = () => {
230
230
  // console.log(e);
231
231
  this.setCache('isClickToHide', false);
232
- this.delayShow();
232
+ this.getProp('condition') !== false && this.delayShow();
233
233
  // this.show('trigger');
234
234
  };
235
235
  triggerEventSet[eventNames.mouseLeave] = () => {
@@ -240,7 +240,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
240
240
  // bind focus to hover trigger for a11y
241
241
  triggerEventSet[eventNames.focus] = () => {
242
242
  const { disableFocusListener } = this.getProps();
243
- !disableFocusListener && this.delayShow();
243
+ this.getProp('condition') !== false && !disableFocusListener && this.delayShow();
244
244
  };
245
245
  triggerEventSet[eventNames.blur] = () => {
246
246
  const { disableFocusListener } = this.getProps();
@@ -259,7 +259,7 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
259
259
  return;
260
260
  }
261
261
 
262
- this.delayShow();
262
+ this.getProp('condition') !== false && this.delayShow();
263
263
  };
264
264
  }
265
265
  break;
@@ -269,6 +269,9 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
269
269
  break;
270
270
  case 'contextMenu':
271
271
  triggerEventSet[eventNames.contextMenu] = (e) => {
272
+ if (this.getProp('condition') === false) {
273
+ return;
274
+ }
272
275
  e.preventDefault();
273
276
  this.show();
274
277
  };
@@ -10,6 +10,8 @@ const strings = {
10
10
  TYPE_TREE_TO_LIST: 'treeList',
11
11
  };
12
12
 
13
- const numbers = {};
13
+ const numbers = {
14
+ DEFAULT_PAGE_SIZE: 10,
15
+ };
14
16
 
15
17
  export { cssClasses, strings, numbers };
@@ -40,7 +40,9 @@ export interface TransferAdapter<P = Record<string, any>, S = Record<string, any
40
40
  notifyDeselect: (items: BasicDataItem) => void;
41
41
  updateInput: (input: string) => void;
42
42
  updateSearchResult: (searchResult: Set<number | string>) => void;
43
- searchTree: (keyword: string) => void
43
+ searchTree: (keyword: string) => void;
44
+ updateCurrentPage: (currentPage: number) => void;
45
+ notifyPageChange: (currentPage: number) => void;
44
46
  }
45
47
 
46
48
  export default class TransferFoundation<P = Record<string, any>, S = Record<string, any>> extends BaseFoundation<TransferAdapter<P, S>> {
@@ -253,4 +255,9 @@ export default class TransferFoundation<P = Record<string, any>, S = Record<stri
253
255
  this._notifyChange(newSelectedItems);
254
256
  }
255
257
 
258
+ handlePageChange(currentPage: number) {
259
+ this._adapter.updateCurrentPage(currentPage);
260
+ this._adapter.notifyPageChange(currentPage);
261
+ }
262
+
256
263
  }
@@ -118,6 +118,16 @@ $module: #{$prefix}-transfer;
118
118
  flex-grow: 1;
119
119
  }
120
120
 
121
+ &-pagination {
122
+ padding-top: $spacing-base-tight;
123
+ padding-bottom: $spacing-base-tight;
124
+ padding-left: $spacing-base-tight;
125
+ padding-right: $spacing-base-tight;
126
+ flex-shrink: 0;
127
+ display: flex;
128
+ justify-content: center;
129
+ }
130
+
121
131
  &-empty {
122
132
  height: $height-transfer_left_empty;
123
133
  }
@@ -3,7 +3,7 @@
3
3
  * https://github.com/react-component/tree
4
4
  */
5
5
 
6
- import { isUndefined, difference, pick, get } from 'lodash';
6
+ import { isUndefined, difference, pick, get, throttle } from 'lodash';
7
7
  import BaseFoundation, { DefaultAdapter } from '../base/foundation';
8
8
  import {
9
9
  flattenTreeData,
@@ -322,11 +322,35 @@ export interface TreeAdapter extends DefaultAdapter<BasicTreeProps, BasicTreeInn
322
322
 
323
323
  export default class TreeFoundation extends BaseFoundation<TreeAdapter, BasicTreeProps, BasicTreeInnerData> {
324
324
  delayedDragEnterLogic: any;
325
+ throttledDragOverUpdate: ReturnType<typeof throttle>;
325
326
 
326
327
  constructor(adapter: TreeAdapter) {
327
328
  super({
328
329
  ...adapter,
329
330
  });
331
+ // Throttle drag over state updates to improve performance during fast dragging
332
+ // 16ms ≈ 60fps, ensuring smooth updates without excessive re-renders
333
+ this.throttledDragOverUpdate = throttle((dropPosition: number) => {
334
+ this._adapter.updateState({
335
+ dropPosition,
336
+ });
337
+ }, 16);
338
+ }
339
+
340
+ destroy() {
341
+ super.destroy();
342
+ // Cancel any pending throttled updates
343
+ if (this.throttledDragOverUpdate) {
344
+ this.throttledDragOverUpdate.cancel();
345
+ }
346
+
347
+ // Clear pending delayed drag enter timers to avoid updates after unmount
348
+ if (this.delayedDragEnterLogic) {
349
+ Object.keys(this.delayedDragEnterLogic).forEach(key => {
350
+ clearTimeout(this.delayedDragEnterLogic[key]);
351
+ });
352
+ this.delayedDragEnterLogic = null;
353
+ }
330
354
  }
331
355
 
332
356
  _isMultiple() {
@@ -863,21 +887,22 @@ export default class TreeFoundation extends BaseFoundation<TreeAdapter, BasicTre
863
887
  return;
864
888
  }
865
889
 
866
- // Update the drag position
890
+ // Update the drag position with throttle to improve performance
867
891
  if (dragNode && eventKey === dragOverNodeKey) {
868
892
  const newPos = calcDropRelativePosition(e, treeNode);
869
893
  if (dropPosition === newPos) {
870
894
  return;
871
895
  }
872
- this._adapter.updateState({
873
- dropPosition: newPos,
874
- });
896
+ // Use throttled update to reduce re-renders during fast dragging
897
+ this.throttledDragOverUpdate(newPos);
875
898
  }
876
899
 
877
900
  this.triggerDragEvent('onDragOver', e, treeNode);
878
901
  }
879
902
 
880
903
  handleNodeDragLeave(e: any, treeNode: BasicTreeNodeData) {
904
+ // Cancel pending throttled updates when leaving a node
905
+ this.throttledDragOverUpdate.cancel();
881
906
  this._adapter.updateState({
882
907
  dragOverNodeKey: '',
883
908
  });
@@ -885,12 +910,16 @@ export default class TreeFoundation extends BaseFoundation<TreeAdapter, BasicTre
885
910
  }
886
911
 
887
912
  handleNodeDragEnd(e: any, treeNode: BasicTreeNodeData) {
913
+ // Flush any pending throttled updates before clearing drag state
914
+ this.throttledDragOverUpdate.flush();
888
915
  this.clearDragState();
889
916
  this.triggerDragEvent('onDragEnd', e, treeNode);
890
917
  this._adapter.setDragNode(null);
891
918
  }
892
919
 
893
920
  handleNodeDrop(e: any, treeNode: BasicTreeNodeData, dragNode: any) {
921
+ // Flush any pending throttled updates to ensure accurate drop position
922
+ this.throttledDragOverUpdate.flush();
894
923
  const { dropPosition, dragNodesKeys } = this.getStates();
895
924
  const { eventKey, pos } = treeNode;
896
925
  this.clearDragState();
@@ -119,6 +119,7 @@ export interface BasicTreeSelectProps extends Pick<BasicTreeProps,
119
119
  outerTopSlot?: any;
120
120
  placeholder?: string;
121
121
  prefix?: any;
122
+ remote?: boolean;
122
123
  searchAutoFocus?: boolean;
123
124
  searchPlaceholder?: string;
124
125
  showSearchClear?: boolean;
@@ -626,7 +627,15 @@ export default class TreeSelectFoundation<P = Record<string, any>, S = Record<st
626
627
  // Input is used as controlled component
627
628
  this._adapter.updateInputValue(sugInput);
628
629
  const { flattenNodes, expandedKeys, selectedKeys, keyEntities, treeData } = this.getStates();
629
- const { showFilteredOnly, filterTreeNode, treeNodeFilterProp, keyMaps } = this.getProps();
630
+ const { showFilteredOnly, filterTreeNode, treeNodeFilterProp, keyMaps, remote } = this.getProps();
631
+
632
+ // When remote is true, skip local filtering, only update inputValue and trigger onSearch callback
633
+ if (remote) {
634
+ this._adapter.notifySearch(sugInput, [], []);
635
+ this._adapter.rePositionDropdown();
636
+ return;
637
+ }
638
+
630
639
  const realFilterProp = treeNodeFilterProp !== 'label' ? treeNodeFilterProp : get(keyMaps, 'label', 'label');
631
640
  const newExpandedKeys: Set<string> = new Set(expandedKeys);
632
641
  let filteredNodes: BasicTreeNodeData[] = [];