@douyinfe/semi-foundation 2.97.0 → 2.99.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 (180) hide show
  1. package/cascader/foundation.ts +3 -1
  2. package/codeHighlight/codeHighlight.scss +1 -1
  3. package/datePicker/foundation.ts +7 -0
  4. package/datePicker/inputFoundation.ts +5 -0
  5. package/form/foundation.ts +50 -4
  6. package/inputNumber/foundation.ts +119 -3
  7. package/jsonViewer/jsonViewer.scss +2 -2
  8. package/lib/cjs/aiChatInput/aiChatInput.css +7 -7
  9. package/lib/cjs/anchor/anchor.css +3 -3
  10. package/lib/cjs/autoComplete/autoComplete.css +1 -1
  11. package/lib/cjs/avatar/avatar.css +5 -5
  12. package/lib/cjs/badge/badge.css +1 -1
  13. package/lib/cjs/breadcrumb/breadcrumb.css +2 -2
  14. package/lib/cjs/calendar/calendar.css +9 -9
  15. package/lib/cjs/cascader/cascader.css +6 -6
  16. package/lib/cjs/cascader/foundation.js +4 -1
  17. package/lib/cjs/checkbox/checkbox.css +2 -2
  18. package/lib/cjs/codeHighlight/codeHighlight.css +1 -1
  19. package/lib/cjs/codeHighlight/codeHighlight.scss +1 -1
  20. package/lib/cjs/collapse/collapse.css +2 -2
  21. package/lib/cjs/datePicker/datePicker.css +8 -8
  22. package/lib/cjs/datePicker/foundation.d.ts +5 -0
  23. package/lib/cjs/datePicker/foundation.js +2 -0
  24. package/lib/cjs/datePicker/inputFoundation.d.ts +5 -0
  25. package/lib/cjs/descriptions/descriptions.css +6 -6
  26. package/lib/cjs/dropdown/dropdown.css +2 -2
  27. package/lib/cjs/form/form.css +4 -4
  28. package/lib/cjs/form/foundation.js +51 -3
  29. package/lib/cjs/hotKeys/hotKeys.css +2 -2
  30. package/lib/cjs/image/image.css +2 -2
  31. package/lib/cjs/input/input.css +8 -8
  32. package/lib/cjs/input/textarea.css +2 -2
  33. package/lib/cjs/inputNumber/foundation.d.ts +15 -0
  34. package/lib/cjs/inputNumber/foundation.js +113 -3
  35. package/lib/cjs/jsonViewer/jsonViewer.css +2 -2
  36. package/lib/cjs/jsonViewer/jsonViewer.scss +2 -2
  37. package/lib/cjs/list/list.css +1 -1
  38. package/lib/cjs/modal/modal.css +1 -1
  39. package/lib/cjs/navigation/navigation.css +2 -2
  40. package/lib/cjs/notification/notification.css +4 -4
  41. package/lib/cjs/pagination/pagination.css +5 -5
  42. package/lib/cjs/popconfirm/popconfirm.css +1 -1
  43. package/lib/cjs/popover/popover.css +5 -5
  44. package/lib/cjs/radio/radio.css +2 -2
  45. package/lib/cjs/scrollList/itemFoundation.js +12 -0
  46. package/lib/cjs/scrollList/scrollList.css +2 -2
  47. package/lib/cjs/select/select.css +6 -6
  48. package/lib/cjs/sideSheet/sideSheet.css +2 -2
  49. package/lib/cjs/sidebar/sidebar.css +11 -11
  50. package/lib/cjs/slider/foundation.js +46 -12
  51. package/lib/cjs/slider/rtl.scss +62 -0
  52. package/lib/cjs/slider/slider.css +45 -0
  53. package/lib/cjs/slider/slider.scss +2 -0
  54. package/lib/cjs/steps/steps.css +11 -11
  55. package/lib/cjs/table/foundation.d.ts +36 -0
  56. package/lib/cjs/table/foundation.js +162 -28
  57. package/lib/cjs/table/rtl.scss +21 -0
  58. package/lib/cjs/table/table.css +38 -2
  59. package/lib/cjs/table/table.scss +49 -0
  60. package/lib/cjs/tabs/tabs.css +2 -2
  61. package/lib/cjs/tag/tag.css +2 -2
  62. package/lib/cjs/tagInput/tagInput.css +2 -2
  63. package/lib/cjs/timePicker/timePicker.css +1 -1
  64. package/lib/cjs/timeline/timeline.css +2 -2
  65. package/lib/cjs/toast/toast.css +1 -1
  66. package/lib/cjs/tooltip/arrow.scss +4 -4
  67. package/lib/cjs/tooltip/foundation.js +72 -5
  68. package/lib/cjs/tooltip/tooltip.css +5 -5
  69. package/lib/cjs/transfer/constants.d.ts +3 -1
  70. package/lib/cjs/transfer/constants.js +3 -1
  71. package/lib/cjs/transfer/foundation.d.ts +3 -0
  72. package/lib/cjs/transfer/foundation.js +4 -0
  73. package/lib/cjs/transfer/transfer.css +14 -5
  74. package/lib/cjs/transfer/transfer.scss +10 -0
  75. package/lib/cjs/tree/foundation.d.ts +3 -0
  76. package/lib/cjs/tree/foundation.js +31 -4
  77. package/lib/cjs/tree/tree.css +1 -1
  78. package/lib/cjs/treeSelect/foundation.d.ts +1 -0
  79. package/lib/cjs/treeSelect/foundation.js +8 -1
  80. package/lib/cjs/treeSelect/treeSelect.css +36 -4
  81. package/lib/cjs/treeSelect/treeSelect.scss +49 -1
  82. package/lib/cjs/typography/typography.css +8 -8
  83. package/lib/cjs/upload/upload.css +8 -8
  84. package/lib/cjs/utils/Store.d.ts +1 -1
  85. package/lib/cjs/utils/Store.js +1 -0
  86. package/lib/es/aiChatInput/aiChatInput.css +7 -7
  87. package/lib/es/anchor/anchor.css +3 -3
  88. package/lib/es/autoComplete/autoComplete.css +1 -1
  89. package/lib/es/avatar/avatar.css +5 -5
  90. package/lib/es/badge/badge.css +1 -1
  91. package/lib/es/breadcrumb/breadcrumb.css +2 -2
  92. package/lib/es/calendar/calendar.css +9 -9
  93. package/lib/es/cascader/cascader.css +6 -6
  94. package/lib/es/cascader/foundation.js +4 -1
  95. package/lib/es/checkbox/checkbox.css +2 -2
  96. package/lib/es/codeHighlight/codeHighlight.css +1 -1
  97. package/lib/es/codeHighlight/codeHighlight.scss +1 -1
  98. package/lib/es/collapse/collapse.css +2 -2
  99. package/lib/es/datePicker/datePicker.css +8 -8
  100. package/lib/es/datePicker/foundation.d.ts +5 -0
  101. package/lib/es/datePicker/foundation.js +2 -0
  102. package/lib/es/datePicker/inputFoundation.d.ts +5 -0
  103. package/lib/es/descriptions/descriptions.css +6 -6
  104. package/lib/es/dropdown/dropdown.css +2 -2
  105. package/lib/es/form/form.css +4 -4
  106. package/lib/es/form/foundation.js +51 -3
  107. package/lib/es/hotKeys/hotKeys.css +2 -2
  108. package/lib/es/image/image.css +2 -2
  109. package/lib/es/input/input.css +8 -8
  110. package/lib/es/input/textarea.css +2 -2
  111. package/lib/es/inputNumber/foundation.d.ts +15 -0
  112. package/lib/es/inputNumber/foundation.js +113 -3
  113. package/lib/es/jsonViewer/jsonViewer.css +2 -2
  114. package/lib/es/jsonViewer/jsonViewer.scss +2 -2
  115. package/lib/es/list/list.css +1 -1
  116. package/lib/es/modal/modal.css +1 -1
  117. package/lib/es/navigation/navigation.css +2 -2
  118. package/lib/es/notification/notification.css +4 -4
  119. package/lib/es/pagination/pagination.css +5 -5
  120. package/lib/es/popconfirm/popconfirm.css +1 -1
  121. package/lib/es/popover/popover.css +5 -5
  122. package/lib/es/radio/radio.css +2 -2
  123. package/lib/es/scrollList/itemFoundation.js +12 -0
  124. package/lib/es/scrollList/scrollList.css +2 -2
  125. package/lib/es/select/select.css +6 -6
  126. package/lib/es/sideSheet/sideSheet.css +2 -2
  127. package/lib/es/sidebar/sidebar.css +11 -11
  128. package/lib/es/slider/foundation.js +46 -12
  129. package/lib/es/slider/rtl.scss +62 -0
  130. package/lib/es/slider/slider.css +45 -0
  131. package/lib/es/slider/slider.scss +2 -0
  132. package/lib/es/steps/steps.css +11 -11
  133. package/lib/es/table/foundation.d.ts +36 -0
  134. package/lib/es/table/foundation.js +162 -28
  135. package/lib/es/table/rtl.scss +21 -0
  136. package/lib/es/table/table.css +38 -2
  137. package/lib/es/table/table.scss +49 -0
  138. package/lib/es/tabs/tabs.css +2 -2
  139. package/lib/es/tag/tag.css +2 -2
  140. package/lib/es/tagInput/tagInput.css +2 -2
  141. package/lib/es/timePicker/timePicker.css +1 -1
  142. package/lib/es/timeline/timeline.css +2 -2
  143. package/lib/es/toast/toast.css +1 -1
  144. package/lib/es/tooltip/arrow.scss +4 -4
  145. package/lib/es/tooltip/foundation.js +72 -5
  146. package/lib/es/tooltip/tooltip.css +5 -5
  147. package/lib/es/transfer/constants.d.ts +3 -1
  148. package/lib/es/transfer/constants.js +3 -1
  149. package/lib/es/transfer/foundation.d.ts +3 -0
  150. package/lib/es/transfer/foundation.js +4 -0
  151. package/lib/es/transfer/transfer.css +14 -5
  152. package/lib/es/transfer/transfer.scss +10 -0
  153. package/lib/es/tree/foundation.d.ts +3 -0
  154. package/lib/es/tree/foundation.js +31 -4
  155. package/lib/es/tree/tree.css +1 -1
  156. package/lib/es/treeSelect/foundation.d.ts +1 -0
  157. package/lib/es/treeSelect/foundation.js +8 -1
  158. package/lib/es/treeSelect/treeSelect.css +36 -4
  159. package/lib/es/treeSelect/treeSelect.scss +49 -1
  160. package/lib/es/typography/typography.css +8 -8
  161. package/lib/es/upload/upload.css +8 -8
  162. package/lib/es/utils/Store.d.ts +1 -1
  163. package/lib/es/utils/Store.js +1 -0
  164. package/package.json +19 -4
  165. package/scrollList/itemFoundation.ts +12 -0
  166. package/slider/foundation.ts +55 -15
  167. package/slider/rtl.scss +62 -0
  168. package/slider/slider.scss +2 -0
  169. package/table/foundation.ts +197 -29
  170. package/table/rtl.scss +21 -0
  171. package/table/table.scss +49 -0
  172. package/tooltip/arrow.scss +4 -4
  173. package/tooltip/foundation.ts +86 -5
  174. package/transfer/constants.ts +3 -1
  175. package/transfer/foundation.ts +8 -1
  176. package/transfer/transfer.scss +10 -0
  177. package/tree/foundation.ts +34 -5
  178. package/treeSelect/foundation.ts +10 -1
  179. package/treeSelect/treeSelect.scss +49 -1
  180. 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/rtl.scss CHANGED
@@ -144,6 +144,27 @@ $module: #{$prefix}-table;
144
144
  border-left: $width-table_base_border $border-table_base-borderStyle $color-table-border-default;
145
145
  }
146
146
  }
147
+
148
+ // Fix #441 (RTL): when horizontal scroll is enabled and table is NOT scrolled to the far left,
149
+ // the outer left border can be outside of viewport (the real border is rendered by the last column cell).
150
+ // Draw an overlay left border on the container to keep it visible.
151
+ // When scrolled to the far left, `.semi-table-scroll-position-left` exists and we should not draw it.
152
+ &:not(.#{$module}-scroll-position-left) {
153
+ & > .#{$module}-container {
154
+ &::after {
155
+ content: '';
156
+ position: absolute;
157
+ top: 0;
158
+ left: 0;
159
+ bottom: 0;
160
+ width: $width-table_base_border;
161
+ background-color: $color-table-border-default;
162
+ display: block;
163
+ z-index: $z-table_fixed_column + 2;
164
+ pointer-events: none;
165
+ }
166
+ }
167
+ }
147
168
  }
148
169
 
149
170
  &-fixed {
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\
@@ -538,6 +555,35 @@ $module: #{$prefix}-table;
538
555
  }
539
556
  }
540
557
 
558
+ // Fix #441: when horizontal scroll is enabled (e.g. scroll.x='101%') and table is NOT scrolled to the far right,
559
+ // the outer right border can be outside of viewport because the real right border is rendered by the
560
+ // last column cell border (which is scrollable). Draw an overlay right border on the container to keep it visible.
561
+ // When scrolled to the far right, `.semi-table-scroll-position-right` exists and we should not draw it to avoid
562
+ // double borders.
563
+ &:not(.#{$module}-scroll-position-right) {
564
+ & > .#{$module}-container {
565
+ &::after {
566
+ content: '';
567
+ position: absolute;
568
+ top: 0;
569
+ right: 0;
570
+ bottom: 0;
571
+ width: $width-table_base_border;
572
+ background-color: $color-table-border-default;
573
+ display: block;
574
+ // Make sure the overlay border stays above table content/fixed columns
575
+ z-index: $z-table_fixed_column + 2;
576
+ pointer-events: none;
577
+ }
578
+
579
+ // Ensure header shows a visible right border even when the container overlay
580
+ // is covered by native scrollbars in some browsers.
581
+ & > .#{$module}-header {
582
+ box-shadow: inset -$width-table_base_border 0 0 0 $color-table-border-default;
583
+ }
584
+ }
585
+ }
586
+
541
587
  :where(& > .#{$module}-container) {
542
588
  & > .#{$module}-body > .#{$module}-placeholder {
543
589
  border-right: $width-table_base_border $border-table_base-borderStyle $color-table-border-default;
@@ -572,6 +618,9 @@ $module: #{$prefix}-table;
572
618
  position: sticky;
573
619
  left: 0px;
574
620
  z-index: 1;
621
+ // In bordered mode, placeholder may receive an extra side border to complete the outer frame.
622
+ // Use border-box to avoid 1px horizontal overflow (and unwanted horizontal scrollbar) in empty data + scroll.y cases.
623
+ box-sizing: border-box;
575
624
  padding: #{$spacing-table-paddingY} #{$spacing-table-paddingX};
576
625
  color: $color-table_placeholder-text-default;
577
626
  font-size: #{$font-table_base-fontSize};
@@ -23,7 +23,7 @@
23
23
 
24
24
  &[x-placement='top'] {
25
25
  .#{$module-icon} {
26
- left: 50%;
26
+ left: var(--semi-tooltip-arrow-offset-x, 50%);
27
27
  transform: translateX(-50%);
28
28
  bottom: (-$height-tooltip_arrow + $spacing-tooltip_arrow_offset-y);
29
29
  }
@@ -61,7 +61,7 @@
61
61
  width: $width-tooltip_arrow_vertical;
62
62
  height: $height-tooltip_arrow_vertical;
63
63
  right: (-$width-tooltip_arrow_vertical + $spacing-tooltip_arrow_offset-x);
64
- top: 50%;
64
+ top: var(--semi-tooltip-arrow-offset-y, 50%);
65
65
  transform: translateY(-50%);
66
66
  }
67
67
 
@@ -93,7 +93,7 @@
93
93
  width: $width-tooltip_arrow_vertical;
94
94
  height: $height-tooltip_arrow_vertical;
95
95
  left: -$width-tooltip_arrow_vertical + $spacing-tooltip_arrow_offset-x;
96
- top: 50%;
96
+ top: var(--semi-tooltip-arrow-offset-y, 50%);
97
97
  transform: translateY(-50%) rotate(180deg);
98
98
  }
99
99
 
@@ -122,7 +122,7 @@
122
122
  &[x-placement='bottom'] {
123
123
  .#{$module-icon} {
124
124
  top: (-$height-tooltip_arrow + $spacing-tooltip_arrow_offset-y);
125
- left: 50%;
125
+ left: var(--semi-tooltip-arrow-offset-x, 50%);
126
126
  transform: translateX(-50%) rotate(180deg);
127
127
  }
128
128
 
@@ -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
  };
@@ -671,12 +674,90 @@ export default class Tooltip<P = Record<string, any>, S = Record<string, any>> e
671
674
  }
672
675
  }
673
676
 
677
+ // Handle arrowPointAtCenter for center positions (top/bottom/left/right)
678
+ // For center positions, the arrow needs to point at trigger center, not Popover center
679
+ let cssArrowOffsetX: string | undefined;
680
+ let cssArrowOffsetY: string | undefined;
681
+
682
+ if (showArrow) {
683
+ const isCenterPosition = ['top', 'bottom', 'left', 'right'].includes(position);
684
+
685
+ if (isCenterPosition) {
686
+ if (arrowPointAtCenter) {
687
+ // arrowPointAtCenter=true: arrow should point at trigger center
688
+ // Calculate arrow position relative to Popover left/top edge
689
+
690
+ if ((position === 'top' || position === 'bottom') && wrapperRect.width > 0) {
691
+ // Popover center is at `left`, trigger center is at `middleX`
692
+ // Arrow position from Popover left edge:
693
+ // = middleX - (left - wrapperRect.width/2)
694
+ // = middleX - left + wrapperRect.width/2
695
+ // Percentage: (middleX - left) / wrapperRect.width + 0.5
696
+ const arrowOffsetPercent = (middleX - left) / wrapperRect.width + 0.5;
697
+
698
+ // Clamp to valid range to prevent arrow going outside Popover
699
+ const minOffset = (horizontalArrowWidth / 2 + positionOffsetX) / wrapperRect.width;
700
+ const maxOffset = 1 - minOffset;
701
+ const clampedOffset = Math.max(minOffset, Math.min(maxOffset, arrowOffsetPercent));
702
+
703
+ // Only set CSS variable if different from default 50%
704
+ if (Math.abs(clampedOffset - 0.5) > 0.01) {
705
+ cssArrowOffsetX = `${clampedOffset * 100}%`;
706
+ }
707
+ }
708
+
709
+ if ((position === 'left' || position === 'right') && wrapperRect.height > 0) {
710
+ const arrowOffsetPercent = (middleY - top) / wrapperRect.height + 0.5;
711
+
712
+ const minOffset = (verticalArrowHeight / 2 + positionOffsetY) / wrapperRect.height;
713
+ const maxOffset = 1 - minOffset;
714
+ const clampedOffset = Math.max(minOffset, Math.min(maxOffset, arrowOffsetPercent));
715
+
716
+ if (Math.abs(clampedOffset - 0.5) > 0.01) {
717
+ cssArrowOffsetY = `${clampedOffset * 100}%`;
718
+ }
719
+ }
720
+ } else {
721
+ // arrowPointAtCenter=false: arrow should be at Popover edge
722
+ // Determine which edge based on trigger position in viewport
723
+
724
+ if ((position === 'top' || position === 'bottom') && wrapperRect.width > 0) {
725
+ const offsetXWithArrow = positionOffsetX + horizontalArrowWidth / 2;
726
+
727
+ if (isTriggerNearLeft) {
728
+ cssArrowOffsetX = `${(offsetXWithArrow / wrapperRect.width) * 100}%`;
729
+ } else {
730
+ cssArrowOffsetX = `${((wrapperRect.width - offsetXWithArrow) / wrapperRect.width) * 100}%`;
731
+ }
732
+ }
733
+
734
+ if ((position === 'left' || position === 'right') && wrapperRect.height > 0) {
735
+ const offsetYWithArrow = positionOffsetY + verticalArrowHeight / 2;
736
+
737
+ if (isTriggerNearTop) {
738
+ cssArrowOffsetY = `${(offsetYWithArrow / wrapperRect.height) * 100}%`;
739
+ } else {
740
+ cssArrowOffsetY = `${((wrapperRect.height - offsetYWithArrow) / wrapperRect.height) * 100}%`;
741
+ }
742
+ }
743
+ }
744
+ }
745
+ }
746
+
674
747
  // The left/top value here must be rounded, otherwise it will cause the small triangle to shake
675
748
  const style: Record<string, string | number> = {
676
749
  left: this._roundPixel(left),
677
750
  top: this._roundPixel(top),
678
751
  };
679
752
 
753
+ // Add CSS variables for arrow positioning
754
+ if (cssArrowOffsetX) {
755
+ style['--semi-tooltip-arrow-offset-x'] = cssArrowOffsetX;
756
+ }
757
+ if (cssArrowOffsetY) {
758
+ style['--semi-tooltip-arrow-offset-y'] = cssArrowOffsetY;
759
+ }
760
+
680
761
  let transform = '';
681
762
 
682
763
  if (translateX != null) {
@@ -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 };