@gmfe/react 2.14.30-alpha.0 → 2.15.4-alpha.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.
- package/package.json +3 -3
- package/src/component/box/box_table.js +17 -12
- package/src/hoc/sticky_layout.js +182 -110
- package/src/index.js +2 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gmfe/react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.15.4-alpha.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "liyatang <liyatang@qq.com>",
|
|
6
6
|
"homepage": "https://github.com/gmfe/gmfe#readme",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@gm-common/tool": "^1.0.0",
|
|
30
|
-
"@gmfe/locales": "^2.
|
|
30
|
+
"@gmfe/locales": "^2.15.4-alpha.0",
|
|
31
31
|
"big.js": "^5.2.2",
|
|
32
32
|
"classnames": "^2.2.5",
|
|
33
33
|
"lodash": "^4.17.14",
|
|
@@ -35,5 +35,5 @@
|
|
|
35
35
|
"prop-types": "^15.7.2",
|
|
36
36
|
"react-window": "^1.8.5"
|
|
37
37
|
},
|
|
38
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "2c13925c3699572748de29cf0256ca1431c30f01"
|
|
39
39
|
}
|
|
@@ -2,7 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import Flex from '../flex'
|
|
5
|
-
import StickyLayout from '
|
|
5
|
+
import { StickyLayout } from '@gmfe/react'
|
|
6
6
|
|
|
7
7
|
// 暂时没什么用
|
|
8
8
|
const Info = props => {
|
|
@@ -19,20 +19,25 @@ Info.propTypes = {
|
|
|
19
19
|
style: PropTypes.object
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const BoxHeader = StickyLayout(
|
|
22
|
+
const BoxHeader = StickyLayout(props => {
|
|
23
23
|
const { info, action, headerProps = {} } = props
|
|
24
24
|
const { className: headerClassName } = headerProps
|
|
25
25
|
|
|
26
|
-
return
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
26
|
+
return (
|
|
27
|
+
<Flex
|
|
28
|
+
{...headerProps}
|
|
29
|
+
className={classNames(
|
|
30
|
+
'gm-box-table-header common-sticky-header',
|
|
31
|
+
headerClassName
|
|
32
|
+
)}
|
|
33
|
+
alignCenter
|
|
34
|
+
>
|
|
35
|
+
<Flex>{info}</Flex>
|
|
36
|
+
<Flex flex />
|
|
37
|
+
<Flex>{action}</Flex>
|
|
38
|
+
</Flex>
|
|
39
|
+
)
|
|
40
|
+
})
|
|
36
41
|
|
|
37
42
|
const BoxTable = props => {
|
|
38
43
|
const { children, className, ...rest } = props
|
package/src/hoc/sticky_layout.js
CHANGED
|
@@ -1,157 +1,229 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @author: stanfer
|
|
3
|
-
* @description:
|
|
3
|
+
* @description: 粘性布局高阶组件,用于实现表格等组件的粘性头部效果
|
|
4
4
|
* @createDate: 2025/11/24 10:05
|
|
5
5
|
* @Version: 1.0
|
|
6
|
-
* @last modify time:
|
|
7
6
|
**/
|
|
8
|
-
import React, { useRef, useEffect } from 'react'
|
|
7
|
+
import React, { useRef, useEffect, useMemo } from 'react'
|
|
8
|
+
import PropTypes from 'prop-types'
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
// 常量配置
|
|
11
|
+
const CONSTANTS = {
|
|
12
|
+
PADDING: {
|
|
13
|
+
RT_THEAD: 10,
|
|
14
|
+
BOX_TABLE_HEADER: 10,
|
|
15
|
+
TABLE_X_THEAD: 8
|
|
16
|
+
},
|
|
17
|
+
Z_INDEX: {
|
|
18
|
+
RT_THEAD: 10,
|
|
19
|
+
STICKY_LAYOUT: 101
|
|
20
|
+
},
|
|
21
|
+
HEIGHT: {
|
|
22
|
+
FULL_TAB: 40,
|
|
23
|
+
TOP_NAV: 50,
|
|
24
|
+
DEFAULT_MAX: '50%'
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// IntersectionObserver 配置
|
|
29
|
+
const OBSERVER_OPTIONS = {
|
|
30
|
+
root: null, // 相对于浏览器视口
|
|
31
|
+
rootMargin: '0px',
|
|
32
|
+
threshold: 0 // 只要有一个像素进入/离开视口就触发
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 哨兵元素样式
|
|
36
|
+
const SENTINEL_STYLE = {
|
|
37
|
+
position: 'absolute',
|
|
38
|
+
left: '0',
|
|
39
|
+
width: '100%',
|
|
40
|
+
height: '1px',
|
|
41
|
+
pointerEvents: 'none',
|
|
42
|
+
backgroundColor: 'transparent'
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 计算最大高度
|
|
47
|
+
* @param {number} heightSum - 高度总和
|
|
48
|
+
* @param {number} fullTabCount - 全屏标签页数量
|
|
49
|
+
* @returns {string} 计算后的最大高度值
|
|
50
|
+
*/
|
|
51
|
+
const calculateMaxHeight = (heightSum, fullTabCount = 0) => {
|
|
52
|
+
const extraHeight =
|
|
53
|
+
fullTabCount * CONSTANTS.HEIGHT.FULL_TAB + CONSTANTS.HEIGHT.TOP_NAV
|
|
54
|
+
return `calc(100vh - ${heightSum + extraHeight}px)`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 设置表格最大高度
|
|
59
|
+
* @param {NodeList|Element[]} elements - 需要设置的元素列表
|
|
60
|
+
* @param {number} heightSum - 高度总和
|
|
61
|
+
* @param {number} fullTabCount - 全屏标签页数量
|
|
62
|
+
*/
|
|
63
|
+
const setElementsMaxHeight = (elements, heightSum, fullTabCount) => {
|
|
64
|
+
const maxHeight = calculateMaxHeight(heightSum, fullTabCount)
|
|
65
|
+
elements.forEach(el => {
|
|
66
|
+
el.style.maxHeight = maxHeight
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 计算粘性元素的总高度
|
|
72
|
+
* @returns {{heightSum: number, tableRoot: Element|null, fullTabCount: number}}
|
|
73
|
+
*/
|
|
74
|
+
const calculateStickyHeight = () => {
|
|
75
|
+
let heightSum = 0
|
|
76
|
+
let tableRoot = null
|
|
77
|
+
|
|
78
|
+
// 查询 DOM 元素
|
|
79
|
+
const fullTab = document.querySelectorAll('.gm-framework-full-tabs')
|
|
80
|
+
const commonStickyHeader = document.querySelectorAll('.common-sticky-header')
|
|
81
|
+
const rtTheadHeader = document.querySelectorAll('.rt-thead')
|
|
82
|
+
|
|
83
|
+
// 处理 rt-thead 元素
|
|
84
|
+
rtTheadHeader.forEach(el => {
|
|
85
|
+
el.style.position = 'sticky'
|
|
86
|
+
el.style.top = '0'
|
|
87
|
+
el.style.zIndex = CONSTANTS.Z_INDEX.RT_THEAD
|
|
88
|
+
heightSum += el.offsetHeight + CONSTANTS.PADDING.RT_THEAD
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// 处理 common-sticky-header 元素
|
|
92
|
+
commonStickyHeader.forEach(item => {
|
|
93
|
+
if (fullTab.length) {
|
|
94
|
+
const currentSticky = document.querySelector('.common-sticky-layout')
|
|
95
|
+
if (currentSticky) {
|
|
96
|
+
currentSticky.style.zIndex = CONSTANTS.Z_INDEX.STICKY_LAYOUT
|
|
97
|
+
}
|
|
14
98
|
}
|
|
15
99
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
100
|
+
if (item.className.includes('gm-box-table-header')) {
|
|
101
|
+
heightSum += item.offsetHeight + CONSTANTS.PADDING.BOX_TABLE_HEADER
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (item.className.includes('gm-table-x-thead')) {
|
|
105
|
+
heightSum += item.offsetHeight + CONSTANTS.PADDING.TABLE_X_THEAD
|
|
106
|
+
tableRoot = item.parentElement?.parentElement || null
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
heightSum,
|
|
112
|
+
tableRoot,
|
|
113
|
+
fullTabCount: fullTab.length
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 处理顶部哨兵进入视口
|
|
119
|
+
*/
|
|
120
|
+
const handleTopSentinelEnter = currentStickyRef => {
|
|
121
|
+
const { heightSum, tableRoot, fullTabCount } = calculateStickyHeight()
|
|
122
|
+
|
|
123
|
+
// 设置 rt-table 元素的最大高度
|
|
124
|
+
const rtTable = document.querySelectorAll('.rt-table')
|
|
125
|
+
if (rtTable.length) {
|
|
126
|
+
setElementsMaxHeight(rtTable, heightSum, fullTabCount)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 设置 tableRoot 的最大高度
|
|
130
|
+
if (tableRoot) {
|
|
131
|
+
tableRoot.style.maxHeight = calculateMaxHeight(heightSum, fullTabCount)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 处理顶部哨兵离开视口
|
|
137
|
+
*/
|
|
138
|
+
const handleTopSentinelLeave = currentStickyRef => {
|
|
139
|
+
if (currentStickyRef.current) {
|
|
140
|
+
currentStickyRef.current.style.maxHeight = CONSTANTS.HEIGHT.DEFAULT_MAX
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function StickyLayout(Component) {
|
|
145
|
+
const StickyLayout = ({ sticky, ...rest }) => {
|
|
146
|
+
const currentStickyRef = useRef(null)
|
|
147
|
+
const topSentinelRef = useRef(null)
|
|
148
|
+
const bottomSentinelRef = useRef(null)
|
|
149
|
+
const observerRef = useRef(null)
|
|
150
|
+
|
|
151
|
+
// 哨兵元素样式
|
|
152
|
+
const topSentinelStyle = useMemo(
|
|
153
|
+
() => ({ ...SENTINEL_STYLE, top: '0' }),
|
|
154
|
+
[]
|
|
155
|
+
)
|
|
156
|
+
const bottomSentinelStyle = useMemo(
|
|
157
|
+
() => ({ ...SENTINEL_STYLE, bottom: '0' }),
|
|
158
|
+
[]
|
|
159
|
+
)
|
|
21
160
|
|
|
22
161
|
useEffect(() => {
|
|
23
|
-
if (!currentStickyRef.current) return
|
|
162
|
+
if (!sticky || !currentStickyRef.current) return undefined
|
|
24
163
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
threshold: 0, // 只要有一个像素进入/离开视口就触发
|
|
29
|
-
};
|
|
164
|
+
if (typeof window.IntersectionObserver === 'undefined') {
|
|
165
|
+
return undefined
|
|
166
|
+
}
|
|
30
167
|
|
|
31
|
-
observerRef.current = new IntersectionObserver(
|
|
32
|
-
entries.forEach(
|
|
33
|
-
const target = entry.target
|
|
168
|
+
observerRef.current = new window.IntersectionObserver(entries => {
|
|
169
|
+
entries.forEach(entry => {
|
|
170
|
+
const target = entry.target
|
|
34
171
|
|
|
35
172
|
if (target === topSentinelRef.current) {
|
|
36
173
|
if (entry.isIntersecting) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const commonStickyHeader = document.querySelectorAll('.common-sticky-header')
|
|
41
|
-
const rtTable = document.querySelectorAll('.rt-table')
|
|
42
|
-
const rtTheadHeader = document.querySelectorAll('.rt-thead')
|
|
43
|
-
rtTheadHeader.forEach((el) => {
|
|
44
|
-
el.style.position = 'sticky';
|
|
45
|
-
el.style.top = 0;
|
|
46
|
-
el.style.zIndex = 10;
|
|
47
|
-
heightSum += el.offsetHeight + 10; // 元素本身高 + 上下 10 padding
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
commonStickyHeader.forEach((item, idx) => {
|
|
51
|
-
if (fullTab.length) {
|
|
52
|
-
currentStickyRef.current.style.zIndex = 101;
|
|
53
|
-
}
|
|
54
|
-
// console.log(item.className);
|
|
55
|
-
if (item.className.includes('gm-box-table-header')) { // 表格筛选项
|
|
56
|
-
heightSum += item.offsetHeight + 10; // 元素本身高 + 上 10 padding
|
|
57
|
-
// console.log(idx, item.getBoundingClientRect(), 'gm-box-table-header: ', item.getBoundingClientRect().height + 20);
|
|
58
|
-
}
|
|
59
|
-
if (item.className.includes('gm-table-x-thead')) { //
|
|
60
|
-
heightSum += item.offsetHeight + 8; // 元素本身高 + 上 8 padding
|
|
61
|
-
tableRoot = item.parentElement.parentElement;
|
|
62
|
-
// console.log(idx, item.getBoundingClientRect(), 'gm-table-x-thead: ', item.getBoundingClientRect().height + 16);
|
|
63
|
-
}
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
if (rtTable.length) {
|
|
67
|
-
rtTable.forEach((el) => {
|
|
68
|
-
if (fullTab.length) {
|
|
69
|
-
el.style.maxHeight = `calc(100vh - ${heightSum + (fullTab.length * 40) + 50}px)`; // +50 顶部导航栏
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
el.style.maxHeight = `calc(100vh - ${heightSum + 50}px)`; // +50 顶部导航栏
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
if (tableRoot) {
|
|
76
|
-
if (fullTab.length) {
|
|
77
|
-
tableRoot.style.maxHeight = `calc(100vh - ${heightSum + (fullTab.length * 40) + 50}px)`; // +50 顶部导航栏
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
tableRoot.style.maxHeight = `calc(100vh - ${heightSum + 50}px)`; // +50 顶部导航栏
|
|
81
|
-
}
|
|
82
|
-
// currentStickyRef.current.style.maxHeight = 'unset';
|
|
83
|
-
// console.log('currentStickyRef 实例:', currentStickyRef.current.style);
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
currentStickyRef.current.style.maxHeight = '50%';
|
|
87
|
-
// console.log('顶部离开 <========');
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (target === bottomSentinelRef.current) {
|
|
91
|
-
if (entry.isIntersecting) {
|
|
92
|
-
// console.log('=======> 底部进入');
|
|
93
|
-
return;
|
|
174
|
+
handleTopSentinelEnter(currentStickyRef)
|
|
175
|
+
} else {
|
|
176
|
+
handleTopSentinelLeave(currentStickyRef)
|
|
94
177
|
}
|
|
95
|
-
// console.log('底部离开 <========');
|
|
96
178
|
}
|
|
97
|
-
})
|
|
98
|
-
},
|
|
179
|
+
})
|
|
180
|
+
}, OBSERVER_OPTIONS)
|
|
99
181
|
|
|
182
|
+
// 观察哨兵元素
|
|
100
183
|
if (topSentinelRef.current) {
|
|
101
|
-
observerRef.current.observe(topSentinelRef.current)
|
|
184
|
+
observerRef.current.observe(topSentinelRef.current)
|
|
102
185
|
}
|
|
103
186
|
if (bottomSentinelRef.current) {
|
|
104
|
-
observerRef.current.observe(bottomSentinelRef.current)
|
|
187
|
+
observerRef.current.observe(bottomSentinelRef.current)
|
|
105
188
|
}
|
|
106
189
|
|
|
107
190
|
return () => {
|
|
108
191
|
if (observerRef.current) {
|
|
109
|
-
observerRef.current.disconnect()
|
|
192
|
+
observerRef.current.disconnect()
|
|
110
193
|
}
|
|
111
|
-
}
|
|
112
|
-
}, [])
|
|
194
|
+
}
|
|
195
|
+
}, [sticky])
|
|
196
|
+
|
|
197
|
+
if (!sticky) {
|
|
198
|
+
return <Component {...rest} />
|
|
199
|
+
}
|
|
113
200
|
|
|
114
201
|
return (
|
|
115
202
|
<div ref={currentStickyRef} className='common-sticky-layout'>
|
|
116
203
|
<div
|
|
117
204
|
ref={topSentinelRef}
|
|
118
|
-
sentinel=
|
|
119
|
-
style={
|
|
120
|
-
position: 'absolute',
|
|
121
|
-
top: '0',
|
|
122
|
-
left: '0',
|
|
123
|
-
width: '100%',
|
|
124
|
-
height: '1px',
|
|
125
|
-
pointerEvents: 'none',
|
|
126
|
-
backgroundColor: 'transparent',
|
|
127
|
-
}}
|
|
205
|
+
data-sentinel='topSentinel'
|
|
206
|
+
style={topSentinelStyle}
|
|
128
207
|
/>
|
|
129
208
|
<Component {...rest} />
|
|
130
209
|
<div
|
|
131
210
|
ref={bottomSentinelRef}
|
|
132
|
-
sentinel=
|
|
133
|
-
style={
|
|
134
|
-
position: 'absolute',
|
|
135
|
-
bottom: '0',
|
|
136
|
-
left: '0',
|
|
137
|
-
width: '100%',
|
|
138
|
-
height: '1px',
|
|
139
|
-
pointerEvents: 'none',
|
|
140
|
-
backgroundColor: 'transparent',
|
|
141
|
-
}}
|
|
211
|
+
data-sentinel='bottomSentinel'
|
|
212
|
+
style={bottomSentinelStyle}
|
|
142
213
|
/>
|
|
143
214
|
</div>
|
|
144
215
|
)
|
|
145
216
|
}
|
|
146
217
|
|
|
147
218
|
StickyLayout.defaultProps = {
|
|
148
|
-
// sticky: {
|
|
149
|
-
// offsetHeight: 0,
|
|
150
|
-
// },
|
|
151
219
|
sticky: false
|
|
152
220
|
}
|
|
153
221
|
|
|
222
|
+
StickyLayout.propTypes = {
|
|
223
|
+
sticky: PropTypes.oneOfType([PropTypes.bool, PropTypes.object])
|
|
224
|
+
}
|
|
225
|
+
|
|
154
226
|
return StickyLayout
|
|
155
227
|
}
|
|
156
228
|
|
|
157
|
-
export default
|
|
229
|
+
export default StickyLayout
|
package/src/index.js
CHANGED
|
@@ -133,8 +133,8 @@ Object.assign(Checkbox, {
|
|
|
133
133
|
Object.assign(Select, {
|
|
134
134
|
Option
|
|
135
135
|
})
|
|
136
|
-
|
|
137
136
|
export {
|
|
137
|
+
StickyLayout,
|
|
138
138
|
// 库之间用
|
|
139
139
|
EVENT_TYPE,
|
|
140
140
|
PaginationBase,
|
|
@@ -234,6 +234,5 @@ export {
|
|
|
234
234
|
Selection,
|
|
235
235
|
TreeV2,
|
|
236
236
|
RecommendInput,
|
|
237
|
-
PicturePreview
|
|
238
|
-
StickyLayout
|
|
237
|
+
PicturePreview
|
|
239
238
|
}
|