@rainbow-oh/yee-c 0.9.2 → 0.11.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 (162) hide show
  1. package/dist/cjs/Button/demo/icon.js +1 -1
  2. package/dist/cjs/Input/style/index.less +1 -0
  3. package/dist/cjs/Table/demo/resizable.d.ts +3 -0
  4. package/dist/cjs/Table/demo/resizable.js +143 -0
  5. package/dist/cjs/Table/hooks/useColumnResize.d.ts +30 -0
  6. package/dist/cjs/Table/hooks/useColumnResize.js +217 -0
  7. package/dist/cjs/Table/hooks/useColumns.d.ts +3 -1
  8. package/dist/cjs/Table/hooks/useColumns.js +14 -3
  9. package/dist/cjs/Table/interface.d.ts +49 -0
  10. package/dist/cjs/Table/style/index.less +42 -0
  11. package/dist/cjs/Table/table.js +62 -9
  12. package/dist/cjs/Table/theader/cell.js +46 -4
  13. package/dist/cjs/Table/util/index.d.ts +19 -1
  14. package/dist/cjs/Table/util/index.js +22 -0
  15. package/dist/cjs/Tabs/demo/overflow-center.d.ts +3 -0
  16. package/dist/cjs/Tabs/demo/overflow-center.js +54 -0
  17. package/dist/cjs/Tabs/hooks/useNavListMove.d.ts +2 -1
  18. package/dist/cjs/Tabs/hooks/useNavListMove.js +51 -16
  19. package/dist/cjs/Tabs/hooks/useTabScroll.d.ts +1 -0
  20. package/dist/cjs/Tabs/hooks/useTabScroll.js +5 -1
  21. package/dist/cjs/Tabs/interface.d.ts +10 -1
  22. package/dist/cjs/Tabs/style/index.less +5 -0
  23. package/dist/cjs/Tabs/tab-header/index.d.ts +1 -0
  24. package/dist/cjs/Tabs/tab-header/index.js +11 -3
  25. package/dist/cjs/Tabs/tabs.js +4 -1
  26. package/dist/cjs/Upload/demo/auto-upload.d.ts +3 -0
  27. package/dist/cjs/Upload/demo/auto-upload.js +65 -0
  28. package/dist/cjs/Upload/demo/basic.js +5 -10
  29. package/dist/cjs/Upload/demo/controlled.js +4 -4
  30. package/dist/cjs/Upload/demo/pictureWall.js +14 -13
  31. package/dist/cjs/Upload/dragger.d.ts +2 -1
  32. package/dist/cjs/Upload/dragger.js +32 -21
  33. package/dist/cjs/Upload/index.d.ts +1 -1
  34. package/dist/cjs/Upload/interface.d.ts +67 -120
  35. package/dist/cjs/Upload/style/index.less +40 -13
  36. package/dist/cjs/Upload/upload.d.ts +2 -2
  37. package/dist/cjs/Upload/upload.js +435 -185
  38. package/dist/cjs/Upload/uploaded-list/index.d.ts +2 -1
  39. package/dist/cjs/Upload/uploaded-list/index.js +6 -3
  40. package/dist/cjs/Upload/uploaded-list/uploaded-item.d.ts +6 -3
  41. package/dist/cjs/Upload/uploaded-list/uploaded-item.js +127 -138
  42. package/dist/cjs/locale/interface.d.ts +1 -0
  43. package/dist/cjs/locale/lang/en_US.js +2 -1
  44. package/dist/cjs/locale/lang/ja_JP.js +2 -1
  45. package/dist/cjs/locale/lang/zh_CN.js +2 -1
  46. package/dist/cjs/locale/lang/zh_TW.js +2 -1
  47. package/dist/cjs/style/size.css +2 -1
  48. package/dist/cjs/style/themes.css +1 -1
  49. package/dist/cjs/style/variables.css +3 -2
  50. package/dist/cjs/utils/ax/core.d.ts +3 -1
  51. package/dist/cjs/utils/ax/core.js +24 -37
  52. package/dist/cjs/utils/ax/index.d.ts +3 -1
  53. package/dist/cjs/utils/folder-scanner.d.ts +1 -1
  54. package/dist/cjs/utils/folder-scanner.js +43 -34
  55. package/dist/cjs/version.d.ts +1 -1
  56. package/dist/cjs/version.js +1 -1
  57. package/dist/esm/Button/demo/icon.js +7 -4
  58. package/dist/esm/Input/style/index.less +1 -0
  59. package/dist/esm/Table/demo/resizable.d.ts +3 -0
  60. package/dist/esm/Table/demo/resizable.js +110 -0
  61. package/dist/esm/Table/hooks/useColumnResize.d.ts +30 -0
  62. package/dist/esm/Table/hooks/useColumnResize.js +206 -0
  63. package/dist/esm/Table/hooks/useColumns.d.ts +3 -1
  64. package/dist/esm/Table/hooks/useColumns.js +9 -4
  65. package/dist/esm/Table/interface.d.ts +49 -0
  66. package/dist/esm/Table/style/index.less +42 -0
  67. package/dist/esm/Table/table.js +83 -13
  68. package/dist/esm/Table/theader/cell.js +38 -3
  69. package/dist/esm/Table/util/index.d.ts +19 -1
  70. package/dist/esm/Table/util/index.js +37 -0
  71. package/dist/esm/Tabs/demo/overflow-center.d.ts +3 -0
  72. package/dist/esm/Tabs/demo/overflow-center.js +61 -0
  73. package/dist/esm/Tabs/hooks/useNavListMove.d.ts +2 -1
  74. package/dist/esm/Tabs/hooks/useNavListMove.js +62 -25
  75. package/dist/esm/Tabs/hooks/useTabScroll.d.ts +1 -0
  76. package/dist/esm/Tabs/hooks/useTabScroll.js +9 -0
  77. package/dist/esm/Tabs/interface.d.ts +10 -1
  78. package/dist/esm/Tabs/style/index.less +5 -0
  79. package/dist/esm/Tabs/tab-header/index.d.ts +1 -0
  80. package/dist/esm/Tabs/tab-header/index.js +15 -3
  81. package/dist/esm/Tabs/tabs.js +4 -2
  82. package/dist/esm/Upload/demo/auto-upload.d.ts +3 -0
  83. package/dist/esm/Upload/demo/auto-upload.js +42 -0
  84. package/dist/esm/Upload/demo/basic.js +5 -11
  85. package/dist/esm/Upload/demo/controlled.js +8 -6
  86. package/dist/esm/Upload/demo/pictureWall.js +17 -14
  87. package/dist/esm/Upload/dragger.d.ts +2 -1
  88. package/dist/esm/Upload/dragger.js +10 -5
  89. package/dist/esm/Upload/index.d.ts +1 -1
  90. package/dist/esm/Upload/interface.d.ts +67 -120
  91. package/dist/esm/Upload/style/index.less +40 -13
  92. package/dist/esm/Upload/upload.d.ts +2 -2
  93. package/dist/esm/Upload/upload.js +573 -271
  94. package/dist/esm/Upload/uploaded-list/index.d.ts +2 -1
  95. package/dist/esm/Upload/uploaded-list/index.js +9 -4
  96. package/dist/esm/Upload/uploaded-list/uploaded-item.d.ts +6 -3
  97. package/dist/esm/Upload/uploaded-list/uploaded-item.js +145 -150
  98. package/dist/esm/locale/interface.d.ts +1 -0
  99. package/dist/esm/locale/lang/en_US.js +2 -1
  100. package/dist/esm/locale/lang/ja_JP.js +2 -1
  101. package/dist/esm/locale/lang/zh_CN.js +2 -1
  102. package/dist/esm/locale/lang/zh_TW.js +2 -1
  103. package/dist/esm/style/size.css +2 -1
  104. package/dist/esm/style/themes.css +1 -1
  105. package/dist/esm/style/variables.css +3 -2
  106. package/dist/esm/utils/ax/core.d.ts +3 -1
  107. package/dist/esm/utils/ax/core.js +32 -40
  108. package/dist/esm/utils/ax/index.d.ts +3 -1
  109. package/dist/esm/utils/folder-scanner.d.ts +1 -1
  110. package/dist/esm/utils/folder-scanner.js +76 -75
  111. package/dist/esm/version.d.ts +1 -1
  112. package/dist/esm/version.js +1 -1
  113. package/dist/umd/yee-c.min.css +2 -2
  114. package/dist/umd/yee-c.min.js +1 -1
  115. package/package.json +1 -1
  116. package/src/Button/demo/icon.tsx +5 -3
  117. package/src/Input/style/index.less +1 -0
  118. package/src/Table/demo/resizable.tsx +106 -0
  119. package/src/Table/hooks/useColumnResize.ts +224 -0
  120. package/src/Table/hooks/useColumns.ts +18 -3
  121. package/src/Table/index.en-US.md +7 -0
  122. package/src/Table/index.zh-CN.md +7 -0
  123. package/src/Table/interface.ts +52 -0
  124. package/src/Table/style/index.less +42 -0
  125. package/src/Table/table-resize.test.tsx +234 -0
  126. package/src/Table/table.tsx +90 -13
  127. package/src/Table/theader/cell.tsx +52 -2
  128. package/src/Table/util/index.ts +44 -1
  129. package/src/Tabs/demo/overflow-center.tsx +35 -0
  130. package/src/Tabs/hooks/useNavListMove.ts +60 -18
  131. package/src/Tabs/hooks/useTabScroll.ts +7 -1
  132. package/src/Tabs/index.en-US.md +33 -31
  133. package/src/Tabs/index.zh-CN.md +33 -31
  134. package/src/Tabs/interface.ts +10 -1
  135. package/src/Tabs/style/index.less +5 -0
  136. package/src/Tabs/tab-header/index.tsx +14 -3
  137. package/src/Tabs/tabs.tsx +2 -0
  138. package/src/Upload/demo/auto-upload.tsx +35 -0
  139. package/src/Upload/demo/basic.tsx +7 -11
  140. package/src/Upload/demo/controlled.tsx +12 -8
  141. package/src/Upload/demo/directory.tsx +3 -4
  142. package/src/Upload/demo/drag.tsx +5 -12
  143. package/src/Upload/demo/multiple.tsx +3 -4
  144. package/src/Upload/demo/pictureWall.tsx +22 -21
  145. package/src/Upload/dragger.tsx +47 -34
  146. package/src/Upload/index.en-US.md +63 -43
  147. package/src/Upload/index.ts +8 -1
  148. package/src/Upload/index.zh-CN.md +62 -42
  149. package/src/Upload/interface.ts +68 -120
  150. package/src/Upload/style/index.less +40 -13
  151. package/src/Upload/upload.tsx +546 -215
  152. package/src/Upload/uploaded-list/index.tsx +9 -5
  153. package/src/Upload/uploaded-list/uploaded-item.tsx +199 -228
  154. package/src/locale/interface.ts +1 -0
  155. package/src/locale/lang/en_US.ts +1 -0
  156. package/src/locale/lang/ja_JP.ts +1 -0
  157. package/src/locale/lang/zh_CN.ts +1 -0
  158. package/src/locale/lang/zh_TW.ts +1 -0
  159. package/src/style/size.css +2 -1
  160. package/src/utils/ax/core.ts +28 -41
  161. package/src/utils/folder-scanner.ts +51 -37
  162. package/src/version.ts +1 -1
@@ -9,6 +9,7 @@ interface UseNavListMoveOptions {
9
9
  direction: 'horizontal' | 'vertical';
10
10
  activeKey: string | number;
11
11
  enabled: boolean;
12
+ align?: 'auto' | 'center';
12
13
  activetab: React.RefObject<HTMLElement | null>;
13
14
  navList: React.RefObject<HTMLDivElement | null>;
14
15
  navContainer: React.RefObject<HTMLDivElement | null>;
@@ -19,6 +20,7 @@ export function useNavListMove({
19
20
  direction,
20
21
  activeKey,
21
22
  enabled,
23
+ align = 'auto',
22
24
  activetab,
23
25
  navList,
24
26
  navContainer,
@@ -33,29 +35,64 @@ export function useNavListMove({
33
35
  return;
34
36
  }
35
37
 
36
- const [isVisible] = isInContainer(direction, tab, list, container);
37
- if (isVisible) return;
38
+ // 'auto': keep as-is when the active tab is already fully visible.
39
+ // 'center': always re-center, even for tabs already visible on the header.
40
+ if (align !== 'center') {
41
+ const [isVisible] = isInContainer(direction, tab, list, container);
42
+ if (isVisible) return;
43
+ }
44
+
45
+ let moved = false;
38
46
 
39
47
  if (direction === 'horizontal') {
40
- const [x] = getEleTranslate(list);
41
- const tabLeft = tab.offsetLeft;
42
- const tabWidth = tab.offsetWidth;
43
- const containerWidth = container.clientWidth;
48
+ if (align === 'center') {
49
+ const containerWidth = container.clientWidth;
50
+ const maxOffset = list.scrollWidth - containerWidth;
51
+ const tabCenter = tab.offsetLeft + tab.offsetWidth / 2;
44
52
 
45
- let newX = x;
53
+ let newX = Math.round(containerWidth / 2 - tabCenter);
54
+ newX = Math.max(-maxOffset, Math.min(0, newX));
46
55
 
47
- // Tab overflows on the right side
48
- if (tabLeft + tabWidth - Math.abs(x) > containerWidth) {
49
- const overflow = tabLeft + tabWidth - Math.abs(x) - containerWidth;
50
- newX = x - overflow;
51
- }
52
- // Tab overflows on the left side
53
- else if (tabLeft < Math.abs(x)) {
54
- const overflow = Math.abs(x) - tabLeft;
55
- newX = x + overflow;
56
+ const [x] = getEleTranslate(list);
57
+ if (Math.abs(newX - x) > 0.5) {
58
+ setEleTranslate(list, newX);
59
+ moved = true;
60
+ }
61
+ } else {
62
+ const [x] = getEleTranslate(list);
63
+ const tabLeft = tab.offsetLeft;
64
+ const tabWidth = tab.offsetWidth;
65
+ const containerWidth = container.clientWidth;
66
+
67
+ let newX = x;
68
+
69
+ // Tab overflows on the right side
70
+ if (tabLeft + tabWidth - Math.abs(x) > containerWidth) {
71
+ const overflow = tabLeft + tabWidth - Math.abs(x) - containerWidth;
72
+ newX = x - overflow;
73
+ }
74
+ // Tab overflows on the left side
75
+ else if (tabLeft < Math.abs(x)) {
76
+ const overflow = Math.abs(x) - tabLeft;
77
+ newX = x + overflow;
78
+ }
79
+
80
+ setEleTranslate(list, newX);
81
+ moved = true;
56
82
  }
83
+ } else if (align === 'center') {
84
+ const containerHeight = container.clientHeight;
85
+ const maxOffset = container.scrollHeight - containerHeight;
86
+ const tabCenter = tab.offsetTop + tab.offsetHeight / 2;
57
87
 
58
- setEleTranslate(list, newX);
88
+ const newScrollTop = Math.max(
89
+ 0,
90
+ Math.min(maxOffset, Math.round(tabCenter - containerHeight / 2)),
91
+ );
92
+ if (Math.abs(newScrollTop - container.scrollTop) > 0.5) {
93
+ container.scrollTop = newScrollTop;
94
+ moved = true;
95
+ }
59
96
  } else {
60
97
  const tabTop = tab.offsetTop;
61
98
  const tabHeight = tab.offsetHeight;
@@ -66,19 +103,24 @@ export function useNavListMove({
66
103
  if (tabTop + tabHeight - scrollTop >= containerHeight) {
67
104
  const overflow = tabTop + tabHeight - scrollTop - containerHeight;
68
105
  container.scrollTop = scrollTop + overflow;
106
+ moved = true;
69
107
  }
70
108
  // Tab overflows above
71
109
  else if (tabTop < scrollTop) {
72
110
  const overflow = scrollTop - tabTop;
73
111
  container.scrollTop = scrollTop - overflow;
112
+ moved = true;
74
113
  }
75
114
  }
76
115
 
77
- onMoved?.();
116
+ if (moved) {
117
+ onMoved?.();
118
+ }
78
119
  }, [
79
120
  activeKey,
80
121
  enabled,
81
122
  direction,
123
+ align,
82
124
  activetab,
83
125
  navList,
84
126
  navContainer,
@@ -43,6 +43,12 @@ export function useTabScroll({
43
43
 
44
44
  const debouncedUpdateShadow = useDebounceFunction(updateShadow, 200);
45
45
 
46
+ // Refresh dropdown range and shadows after the nav list is moved programmatically
47
+ const refresh = useCallback(() => {
48
+ setRefreshKey((prev) => prev + 1);
49
+ debouncedUpdateShadow();
50
+ }, [debouncedUpdateShadow]);
51
+
46
52
  useEffect(() => {
47
53
  const container = navContainer.current;
48
54
  const list = navList.current;
@@ -99,5 +105,5 @@ export function useTabScroll({
99
105
  updateShadow,
100
106
  ]);
101
107
 
102
- return { refreshKey, updateShadow: debouncedUpdateShadow };
108
+ return { refreshKey, refresh, updateShadow: debouncedUpdateShadow };
103
109
  }
@@ -19,43 +19,45 @@ Tabs make it easy to switch between different views.
19
19
  <code src="./demo/type.tsx" title="Type" description="Different types of tabs"></code>
20
20
  <code src="./demo/editable.tsx" title="Editable" description="Add and remove tabs"></code>
21
21
  <code src="./demo/extra.tsx" title="Extra Content" description="Extra content in tabs"></code>
22
+ <code src="./demo/overflow-center.tsx" title="Center Active Tab on Overflow" description="When tabs overflow into a dropdown, activeAlign=&quot;center&quot; scrolls the tab selected from the dropdown to the middle of the header"></code>
22
23
  <code src="./demo/controlled.tsx" title="Controlled" description="Controlled tabs"></code>
23
24
 
24
25
  ## API
25
26
 
26
27
  ### TabsProps
27
28
 
28
- | Property | Type | Description | Default |
29
- | --- | --- | --- | --- |
30
- | prefixCls | `string` | Custom class name prefix | - |
31
- | className | `string` | Custom root class name | - |
32
- | style | `React.CSSProperties` | Custom root style | - |
33
- | classNames | `Partial<Record<SemanticDOM, string>>` | Custom structure class names | - |
34
- | styles | `Partial<Record<SemanticDOM, React.CSSProperties>>` | Custom structure styles | - |
35
- | type | `'card' \| 'editable-card'` | Tab style | - |
36
- | activeKey | `string \| number` | Current active tab key | - |
37
- | defaultActiveKey | `string \| number` | Default active tab key | - |
38
- | items | `Array<TabItemType>` | Tab items | - |
39
- | position | `'top' \| 'bottom' \| 'left' \| 'right'` | Tab position | `top` |
40
- | headerExtra | `{ prefix?: React.ReactNode \| (() => React.ReactNode); suffix?: React.ReactNode \| (() => React.ReactNode) }` | Extra content in tab bar | - |
41
- | children | `Array<React.ReactElement>` | Children elements | - |
42
- | lazy | `boolean` | Lazy load content | - |
43
- | fixedHeader | `boolean` | Whether to fix header area | `false` |
44
- | onEdit | `(type: 'add' \| 'remove', key?: number \| string) => void` | Edit callback | - |
45
- | onChange | `(key: string \| number) => void` | Change callback | - |
46
- | onTabClick | `(key: string \| number) => void \| boolean` | Tab click callback | - |
47
- | data-* | `string` | - | Supports all data-* attributes, forwarded to root DOM element |
29
+ | Property | Type | Description | Default |
30
+ | ---------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------- |
31
+ | prefixCls | `string` | Custom class name prefix | - |
32
+ | className | `string` | Custom root class name | - |
33
+ | style | `React.CSSProperties` | Custom root style | - |
34
+ | classNames | `Partial<Record<SemanticDOM, string>>` | Custom structure class names | - |
35
+ | styles | `Partial<Record<SemanticDOM, React.CSSProperties>>` | Custom structure styles | - |
36
+ | type | `'card' \| 'editable-card'` | Tab style | - |
37
+ | activeKey | `string \| number` | Current active tab key | - |
38
+ | defaultActiveKey | `string \| number` | Default active tab key | - |
39
+ | items | `Array<TabItemType>` | Tab items | - |
40
+ | position | `'top' \| 'bottom' \| 'left' \| 'right'` | Tab position | `top` |
41
+ | headerExtra | `{ prefix?: React.ReactNode \| (() => React.ReactNode); suffix?: React.ReactNode \| (() => React.ReactNode) }` | Extra content in tab bar | - |
42
+ | children | `Array<React.ReactElement>` | Children elements | - |
43
+ | lazy | `boolean` | Lazy load content | - |
44
+ | fixedHeader | `boolean` | Whether to fix header area | `false` |
45
+ | activeAlign | `'auto' \| 'center'` | How the active tab is aligned when the tab list overflows: `auto` scrolls minimally to the edge (no scrolling when already fully visible); `center` always scrolls it to the middle of the header, including tabs clicked directly on the header | `auto` |
46
+ | onEdit | `(type: 'add' \| 'remove', key?: number \| string) => void` | Edit callback | - |
47
+ | onChange | `(key: string \| number) => void` | Change callback | - |
48
+ | onTabClick | `(key: string \| number) => void \| boolean` | Tab click callback | - |
49
+ | data-\* | `string` | - | Supports all data-\* attributes, forwarded to root DOM element |
48
50
 
49
51
  ### TabItemType
50
52
 
51
- | Property | Type | Description | Default |
52
- | --- | --- | --- | --- |
53
- | key | `string \| number` | Unique key | - |
54
- | label | `React.ReactNode` | Tab label | - |
55
- | icon | `React.ReactNode` | Tab icon | - |
56
- | disabled | `boolean` | Disabled state | - |
57
- | closable | `boolean` | Whether closable (effective when type="editable-card") | `true` |
58
- | badge | `number` | Badge number | - |
59
- | items | `Array<TabItemType>` | Sub tabs | - |
60
- | children | `React.ReactNode` | Tab content | - |
61
- | data-* | `string` | - | Supports all data-* attributes, forwarded to root DOM element |
53
+ | Property | Type | Description | Default |
54
+ | -------- | -------------------- | ------------------------------------------------------ | -------------------------------------------------------------- |
55
+ | key | `string \| number` | Unique key | - |
56
+ | label | `React.ReactNode` | Tab label | - |
57
+ | icon | `React.ReactNode` | Tab icon | - |
58
+ | disabled | `boolean` | Disabled state | - |
59
+ | closable | `boolean` | Whether closable (effective when type="editable-card") | `true` |
60
+ | badge | `number` | Badge number | - |
61
+ | items | `Array<TabItemType>` | Sub tabs | - |
62
+ | children | `React.ReactNode` | Tab content | - |
63
+ | data-\* | `string` | - | Supports all data-\* attributes, forwarded to root DOM element |
@@ -19,43 +19,45 @@ toc: 'content'
19
19
  <code src="./demo/type.tsx" title="类型" description="不同类型的标签页"></code>
20
20
  <code src="./demo/editable.tsx" title="可编辑" description="可添加和删除标签页"></code>
21
21
  <code src="./demo/extra.tsx" title="额外内容" description="标签页中的额外内容"></code>
22
+ <code src="./demo/overflow-center.tsx" title="溢出时激活Tab居中" description="Tab较多出现下拉时,activeAlign=&quot;center&quot; 让从下拉选中的Tab滚动到Header中间"></code>
22
23
  <code src="./demo/controlled.tsx" title="受控" description="受控的标签页"></code>
23
24
 
24
25
  ## API
25
26
 
26
27
  ### TabsProps
27
28
 
28
- | 属性名 | 类型 | 描述 | 默认值 |
29
- | --- | --- | --- | --- |
30
- | prefixCls | `string` | 自定义类名前缀 | - |
31
- | className | `string` | 自定义根类名 | - |
32
- | style | `React.CSSProperties` | 自定义根行内样式 | - |
33
- | classNames | `Partial<Record<SemanticDOM, string>>` | 自定义结构化类名 | - |
34
- | styles | `Partial<Record<SemanticDOM, React.CSSProperties>>` | 自定义结构化样式 | - |
35
- | type | `'card' \| 'editable-card'` | 页签样式 | - |
36
- | activeKey | `string \| number` | 当前激活tab面板的key | - |
37
- | defaultActiveKey | `string \| number` | 默认激活的tab面板的key | - |
38
- | items | `Array<TabItemType>` | tab选项卡内容 | - |
39
- | position | `'top' \| 'bottom' \| 'left' \| 'right'` | 页签位置 | `top` |
40
- | headerExtra | `{ prefix?: React.ReactNode \| (() => React.ReactNode); suffix?: React.ReactNode \| (() => React.ReactNode) }` | tab额外内容 | - |
41
- | children | `Array<React.ReactElement>` | 子元素 | - |
42
- | lazy | `boolean` | 是否懒加载,只有当切换到该tab时才将对应的content加载到dom中 | - |
43
- | fixedHeader | `boolean` | 是否固定Header区域 | `false` |
44
- | onEdit | `(type: 'add' \| 'remove', key?: number \| string) => void` | 编辑Tab时的回调 | - |
45
- | onChange | `(key: string \| number) => void` | Tab改变时的回调 | - |
46
- | onTabClick | `(key: string \| number) => void \| boolean` | 点击Tab时的回调,当返回false时,不会切换Tab, 不会触发onChange | - |
47
- | data-* | `string` | - | 支持所有 data-* 属性,透传到组件根 DOM 元素 |
29
+ | 属性名 | 类型 | 描述 | 默认值 |
30
+ | ---------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
31
+ | prefixCls | `string` | 自定义类名前缀 | - |
32
+ | className | `string` | 自定义根类名 | - |
33
+ | style | `React.CSSProperties` | 自定义根行内样式 | - |
34
+ | classNames | `Partial<Record<SemanticDOM, string>>` | 自定义结构化类名 | - |
35
+ | styles | `Partial<Record<SemanticDOM, React.CSSProperties>>` | 自定义结构化样式 | - |
36
+ | type | `'card' \| 'editable-card'` | 页签样式 | - |
37
+ | activeKey | `string \| number` | 当前激活tab面板的key | - |
38
+ | defaultActiveKey | `string \| number` | 默认激活的tab面板的key | - |
39
+ | items | `Array<TabItemType>` | tab选项卡内容 | - |
40
+ | position | `'top' \| 'bottom' \| 'left' \| 'right'` | 页签位置 | `top` |
41
+ | headerExtra | `{ prefix?: React.ReactNode \| (() => React.ReactNode); suffix?: React.ReactNode \| (() => React.ReactNode) }` | tab额外内容 | - |
42
+ | children | `Array<React.ReactElement>` | 子元素 | - |
43
+ | lazy | `boolean` | 是否懒加载,只有当切换到该tab时才将对应的content加载到dom中 | - |
44
+ | fixedHeader | `boolean` | 是否固定Header区域 | `false` |
45
+ | activeAlign | `'auto' \| 'center'` | Tab溢出时激活tab的对齐方式:`auto` 贴边最小滚动(完全可见时不滚动);`center` 始终滚动到header中间居中,包括直接点击header上可见的tab | `auto` |
46
+ | onEdit | `(type: 'add' \| 'remove', key?: number \| string) => void` | 编辑Tab时的回调 | - |
47
+ | onChange | `(key: string \| number) => void` | Tab改变时的回调 | - |
48
+ | onTabClick | `(key: string \| number) => void \| boolean` | 点击Tab时的回调,当返回false时,不会切换Tab, 不会触发onChange | - |
49
+ | data-\* | `string` | - | 支持所有 data-\* 属性,透传到组件根 DOM 元素 |
48
50
 
49
51
  ### TabItemType
50
52
 
51
- | 属性名 | 类型 | 描述 | 默认值 |
52
- | --- | --- | --- | --- |
53
- | key | `string \| number` | 唯一key对应activeKey | - |
54
- | label | `React.ReactNode` | 选项卡显示内容 | - |
55
- | icon | `React.ReactNode` | 选项卡显示图标 | - |
56
- | disabled | `boolean` | 是否禁用 | - |
57
- | closable | `boolean` | 是否可关闭,当type="editable-card"时生效 | `true` |
58
- | badge | `number` | 角标 | - |
59
- | items | `Array<TabItemType>` | 子tab | - |
60
- | children | `React.ReactNode` | 显示内容 | - |
61
- | data-* | `string` | - | 支持所有 data-* 属性,透传到组件根 DOM 元素 |
53
+ | 属性名 | 类型 | 描述 | 默认值 |
54
+ | -------- | -------------------- | ---------------------------------------- | -------------------------------------------- |
55
+ | key | `string \| number` | 唯一key对应activeKey | - |
56
+ | label | `React.ReactNode` | 选项卡显示内容 | - |
57
+ | icon | `React.ReactNode` | 选项卡显示图标 | - |
58
+ | disabled | `boolean` | 是否禁用 | - |
59
+ | closable | `boolean` | 是否可关闭,当type="editable-card"时生效 | `true` |
60
+ | badge | `number` | 角标 | - |
61
+ | items | `Array<TabItemType>` | 子tab | - |
62
+ | children | `React.ReactNode` | 显示内容 | - |
63
+ | data-\* | `string` | - | 支持所有 data-\* 属性,透传到组件根 DOM 元素 |
@@ -1,6 +1,6 @@
1
1
  import type { DataAttributeProps } from '../utils/types';
2
2
 
3
- type SemanticDOM = 'header' | 'content' | 'track' | 'list' | 'activeBar';
3
+ type SemanticDOM = 'header' | 'content' | 'track' | 'list' | 'activeBar' | 'more';
4
4
 
5
5
  export type TabItemType = {
6
6
  /**
@@ -124,4 +124,13 @@ export interface TabsProps extends DataAttributeProps {
124
124
  * @default false
125
125
  */
126
126
  fixedHeader?: boolean;
127
+ /**
128
+ * How the active tab is aligned when the tab list overflows
129
+ * - 'auto': scroll minimally so the active tab is visible at the edge;
130
+ * no scrolling when it is already fully visible
131
+ * - 'center': always scroll so the active tab is centered in the header,
132
+ * including tabs clicked directly on the header
133
+ * @default 'auto'
134
+ */
135
+ activeAlign?: 'auto' | 'center';
127
136
  }
@@ -439,6 +439,10 @@
439
439
  height: var(--yee-tabs-nav-operations-height);
440
440
  }
441
441
 
442
+ @{pls}-nav-more {
443
+ margin-left: 4px;
444
+ }
445
+
442
446
  // 下拉框
443
447
  @{pls}-operation-container {
444
448
  max-height: var(--yee-tabs-operation-container-max-height);
@@ -452,6 +456,7 @@
452
456
 
453
457
  @{pls}-tab {
454
458
  height: var(--yee-tabs-operation-tab-height);
459
+ margin: 0;
455
460
  padding: var(--yee-tabs-operation-tab-padding);
456
461
  cursor: pointer;
457
462
 
@@ -38,6 +38,7 @@ export interface TabHeaderProps {
38
38
  prefixCls: string;
39
39
  type?: 'card' | 'editable-card';
40
40
  position?: 'top' | 'bottom' | 'left' | 'right';
41
+ activeAlign?: 'auto' | 'center';
41
42
  headerExtra?: {
42
43
  prefix?: React.ReactNode | (() => React.ReactNode);
43
44
  suffix?: React.ReactNode | (() => React.ReactNode);
@@ -53,6 +54,7 @@ const TabHeader: React.FC<TabHeaderProps> = (props) => {
53
54
  prefixCls,
54
55
  type,
55
56
  position = 'top',
57
+ activeAlign = 'auto',
56
58
  headerExtra,
57
59
  classNames,
58
60
  styles,
@@ -80,7 +82,7 @@ const TabHeader: React.FC<TabHeaderProps> = (props) => {
80
82
  });
81
83
 
82
84
  // Scroll handling
83
- const { refreshKey } = useTabScroll({
85
+ const { refreshKey, refresh: refreshNav } = useTabScroll({
84
86
  direction,
85
87
  navContainer,
86
88
  navList,
@@ -97,15 +99,22 @@ const TabHeader: React.FC<TabHeaderProps> = (props) => {
97
99
  enabled: !type, // Only show active bar for non-card types
98
100
  });
99
101
 
102
+ // Recompute dropdown visibility, dropdown range and shadows after the list is moved
103
+ const handleNavMoved = useCallback(() => {
104
+ checkVisibility();
105
+ refreshNav();
106
+ }, [checkVisibility, refreshNav]);
107
+
100
108
  // Navigation list movement
101
109
  useNavListMove({
102
110
  direction,
103
111
  activeKey,
104
112
  enabled: moreOpen,
113
+ align: activeAlign,
105
114
  activetab,
106
115
  navList,
107
116
  navContainer,
108
- onMoved: checkVisibility,
117
+ onMoved: handleNavMoved,
109
118
  });
110
119
 
111
120
  // Listen for container size changes
@@ -212,7 +221,9 @@ const TabHeader: React.FC<TabHeaderProps> = (props) => {
212
221
  {moreOpen && (
213
222
  <DropDownTabs visible={moreOpen} items={items || []} range={range}>
214
223
  <Button
215
- className={`${prefixCls}-nav-more`}
224
+ className={clsx(`${prefixCls}-nav-more`, classNames?.more)}
225
+ style={styles?.more}
226
+ size="small"
216
227
  icon={<Ellipsis size={14} strokeWidth={1.5} />}
217
228
  type="text"
218
229
  aria-label="More tabs"
package/src/Tabs/tabs.tsx CHANGED
@@ -39,6 +39,7 @@ const Tabs: FC<TabsProps> = (baseprops) => {
39
39
  activeKey,
40
40
  defaultActiveKey,
41
41
  fixedHeader,
42
+ activeAlign,
42
43
  onChange,
43
44
  onEdit,
44
45
  onTabClick,
@@ -117,6 +118,7 @@ const Tabs: FC<TabsProps> = (baseprops) => {
117
118
  <TabHeader
118
119
  {...omit(props, ['children', 'className', 'style'])}
119
120
  prefixCls={prefixCls}
121
+ activeAlign={activeAlign}
120
122
  />
121
123
  <TabContent
122
124
  prefixCls={prefixCls}
@@ -0,0 +1,35 @@
1
+ import {
2
+ Button,
3
+ Upload,
4
+ type UploadFile,
5
+ type UploadInstance,
6
+ } from '@rainbow-oh/yee-c';
7
+ import React, { useRef, useState } from 'react';
8
+
9
+ export default () => {
10
+ const uploadRef = useRef<UploadInstance>(null);
11
+ const [fileList, setFileList] = useState<UploadFile[]>([]);
12
+ const readyCount = fileList.filter((file) => file.status === 'ready').length;
13
+
14
+ return (
15
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
16
+ <Upload
17
+ ref={uploadRef}
18
+ autoUpload={false}
19
+ multiple
20
+ action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
21
+ fileList={fileList}
22
+ onChange={({ fileList: nextFileList }) => setFileList(nextFileList)}
23
+ />
24
+ <div>
25
+ <Button
26
+ type="primary"
27
+ disabled={readyCount === 0}
28
+ onClick={() => uploadRef.current?.upload()}
29
+ >
30
+ Start upload ({readyCount})
31
+ </Button>
32
+ </div>
33
+ </div>
34
+ );
35
+ };
@@ -1,11 +1,10 @@
1
- import { Button, Upload } from '@rainbow-oh/yee-c';
1
+ import { Button, Upload, type UploadProps } from '@rainbow-oh/yee-c';
2
2
  import React from 'react';
3
- import type { UploadFile } from '../interface';
4
3
 
5
4
  export default () => {
6
- const props = {
5
+ const props: UploadProps = {
7
6
  action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
8
- onChange({ file, fileList }: { file: UploadFile; fileList: UploadFile[] }) {
7
+ onChange({ file, fileList }) {
9
8
  if (file.status !== 'uploading') {
10
9
  console.log(file, fileList);
11
10
  }
@@ -14,26 +13,23 @@ export default () => {
14
13
  {
15
14
  uid: '1',
16
15
  name: 'xxx.png',
17
- status: 'done',
18
- raw: {} as File,
16
+ status: 'success',
19
17
  size: 123,
20
- percent: 0,
18
+ percent: 100,
21
19
  type: 'image/png',
22
20
  },
23
21
  {
24
22
  uid: '2',
25
23
  name: 'yyy.png',
26
- status: 'done',
27
- raw: {} as File,
24
+ status: 'success',
28
25
  size: 123,
29
- percent: 0,
26
+ percent: 100,
30
27
  type: 'image/png',
31
28
  },
32
29
  ],
33
30
  };
34
31
 
35
32
  return (
36
- // @ts-ignore
37
33
  <Upload {...props}>
38
34
  <Button>Upload</Button>
39
35
  </Upload>
@@ -1,21 +1,25 @@
1
- import { Button, Upload } from '@rainbow-oh/yee-c';
1
+ import {
2
+ Button,
3
+ Upload,
4
+ type UploadFile,
5
+ type UploadProps,
6
+ } from '@rainbow-oh/yee-c';
2
7
  import React, { useState } from 'react';
3
- import type { UploadFile } from '../interface';
4
8
 
5
9
  export default () => {
6
10
  const [fileList, setFileList] = useState<UploadFile[]>([]);
7
11
 
8
- const props = {
12
+ const props: UploadProps = {
9
13
  action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
10
14
  fileList,
11
- onChange({ file, fileList }: { file: UploadFile; fileList: UploadFile[] }) {
12
- setFileList(fileList);
15
+ onChange({ file, fileList: nextFileList }) {
16
+ setFileList(nextFileList);
13
17
  if (file.status !== 'uploading') {
14
- console.log(file, fileList);
18
+ console.log(file, nextFileList);
15
19
  }
16
20
  },
17
- onRemove(file: UploadFile) {
18
- setFileList(fileList.filter((item) => item.uid !== file.uid));
21
+ onRemove(file) {
22
+ setFileList((current) => current.filter((item) => item.uid !== file.uid));
19
23
  return true;
20
24
  },
21
25
  };
@@ -1,12 +1,11 @@
1
- import { Button, Upload } from '@rainbow-oh/yee-c';
1
+ import { Button, Upload, type UploadProps } from '@rainbow-oh/yee-c';
2
2
  import React from 'react';
3
- import type { UploadFile } from '../interface';
4
3
 
5
4
  export default () => {
6
- const props = {
5
+ const props: UploadProps = {
7
6
  action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
8
7
  directory: true,
9
- onChange({ file, fileList }: { file: UploadFile; fileList: UploadFile[] }) {
8
+ onChange({ file, fileList }) {
10
9
  if (file.status !== 'uploading') {
11
10
  console.log(file, fileList);
12
11
  }
@@ -1,23 +1,16 @@
1
- import { Upload } from '@rainbow-oh/yee-c';
1
+ import { Upload, type UploadProps } from '@rainbow-oh/yee-c';
2
2
  import React from 'react';
3
- import type { UploadFile } from '../interface';
4
3
 
5
4
  export default () => {
6
- const props = {
5
+ const props: UploadProps = {
7
6
  action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
8
- type: 'drag' as const,
9
- onChange({ file, fileList }: { file: UploadFile; fileList: UploadFile[] }) {
7
+ type: 'drag',
8
+ onChange({ file, fileList }) {
10
9
  if (file.status !== 'uploading') {
11
10
  console.log(file, fileList);
12
11
  }
13
12
  },
14
13
  };
15
14
 
16
- return (
17
- <Upload.Dragger {...props}>
18
- {/* <div style={{ padding: '20px', border: '1px dashed #ccc' }}>
19
- <p>Click or drag file to this area to upload</p>
20
- </div> */}
21
- </Upload.Dragger>
22
- );
15
+ return <Upload.Dragger {...props} />;
23
16
  };
@@ -1,12 +1,11 @@
1
- import { Button, Upload } from '@rainbow-oh/yee-c';
1
+ import { Button, Upload, type UploadProps } from '@rainbow-oh/yee-c';
2
2
  import React from 'react';
3
- import type { UploadFile } from '../interface';
4
3
 
5
4
  export default () => {
6
- const props = {
5
+ const props: UploadProps = {
7
6
  action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
8
7
  multiple: true,
9
- onChange({ file, fileList }: { file: UploadFile; fileList: UploadFile[] }) {
8
+ onChange({ file, fileList }) {
10
9
  if (file.status !== 'uploading') {
11
10
  console.log(file, fileList);
12
11
  }