@vxe-ui/core 4.4.30 → 4.4.32

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.
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.popperUtils = void 0;
7
+ var _xeUtils = _interopRequireDefault(require("xe-utils"));
8
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
+ /**
10
+ * 获取包裹容器的元素
11
+ */
12
+ function getWrapperElement(panelElem) {
13
+ if (!panelElem) {
14
+ return null;
15
+ }
16
+ const bodyEl = document.body;
17
+ const parentEl = panelElem.parentElement;
18
+ return (parentEl === bodyEl ? document.documentElement : parentEl) || null;
19
+ }
20
+ /**
21
+ * 获取插入到指定容器的元素或字符串
22
+ */
23
+ function getAppendSelector(appendTo) {
24
+ const selectElem = appendTo && _xeUtils.default.isFunction(appendTo) ? appendTo({}) : appendTo;
25
+ return selectElem || 'body';
26
+ }
27
+ /**
28
+ * 获取插入到指定容器的元素
29
+ */
30
+ function getAppendElement(appendTo) {
31
+ const selectElem = getAppendSelector(appendTo);
32
+ if (_xeUtils.default.isString(selectElem)) {
33
+ return document.querySelector(selectElem) || document.body;
34
+ }
35
+ return selectElem;
36
+ }
37
+ const popperUtils = exports.popperUtils = {
38
+ /**
39
+ * 更新位置
40
+ */
41
+ updatePlacement(targetElem, panelElem, options) {
42
+ const {
43
+ defaultTop,
44
+ defaultLeft,
45
+ placement,
46
+ defaultPlacement,
47
+ appendTo,
48
+ teleportTo,
49
+ marginSize,
50
+ isMinWidth
51
+ } = Object.assign({
52
+ appendTo: '',
53
+ teleportTo: false,
54
+ marginSize: 18,
55
+ isMinWidth: true
56
+ }, options);
57
+ let panelPlacement = 'bottom';
58
+ let top = '';
59
+ let bottom = '';
60
+ let left = 0;
61
+ let minWidth = '';
62
+ let arrowLeft = '';
63
+ const stys = {};
64
+ if (panelElem && targetElem) {
65
+ const bodyEl = document.body;
66
+ const parentWrapperEl = getWrapperElement(teleportTo ? getAppendElement(appendTo) : panelElem);
67
+ if (parentWrapperEl) {
68
+ const targetWidth = targetElem.offsetWidth;
69
+ const targetHeight = targetElem.offsetHeight;
70
+ const panelWidth = panelElem.offsetWidth;
71
+ const panelHeight = panelElem.offsetHeight;
72
+ const parentWrapperRect = parentWrapperEl.getBoundingClientRect();
73
+ const panelRect = panelElem.getBoundingClientRect();
74
+ const targetRect = targetElem.getBoundingClientRect();
75
+ const visibleHeight = bodyEl.clientHeight;
76
+ const visibleWidth = bodyEl.clientWidth;
77
+ const parentWrapperScrollLeft = parentWrapperEl.scrollLeft;
78
+ const parentWrapperScrollTop = parentWrapperEl.scrollTop;
79
+ const parentWrapperOffsetLeft = parentWrapperRect.left;
80
+ const parentWrapperOffsetTop = parentWrapperRect.top;
81
+ const targetLeft = targetRect.left;
82
+ const targetTop = targetRect.top;
83
+ minWidth = targetElem.offsetWidth;
84
+ if (teleportTo) {
85
+ left = (defaultLeft || targetLeft - (panelWidth - targetWidth) / 2) - parentWrapperScrollLeft;
86
+ top = (defaultTop || targetTop + targetHeight) - parentWrapperScrollTop;
87
+ if (placement === 'top') {
88
+ panelPlacement = 'top';
89
+ top = targetTop - panelHeight;
90
+ } else if (!placement) {
91
+ if (defaultPlacement === 'top') {
92
+ panelPlacement = 'top';
93
+ if (!defaultTop) {
94
+ top = targetTop - panelHeight;
95
+ }
96
+ // 如果上面不够放,则向下
97
+ if (top < marginSize) {
98
+ panelPlacement = 'bottom';
99
+ top = targetTop + targetHeight;
100
+ }
101
+ // 如果下面不够放,则向上(优先)
102
+ if (top + panelHeight + marginSize > visibleHeight) {
103
+ panelPlacement = 'top';
104
+ top = targetTop - panelHeight;
105
+ }
106
+ } else {
107
+ // 如果下面不够放,则向上
108
+ if (top + panelHeight + marginSize > visibleHeight) {
109
+ panelPlacement = 'top';
110
+ top = targetTop - panelHeight;
111
+ }
112
+ // 如果上面不够放,则向下(优先)
113
+ if (top < marginSize) {
114
+ panelPlacement = 'bottom';
115
+ top = targetTop + targetHeight;
116
+ }
117
+ }
118
+ }
119
+ // 如果溢出右边
120
+ if (left + panelWidth + marginSize > visibleWidth) {
121
+ left -= left + panelWidth + marginSize - visibleWidth;
122
+ }
123
+ // 如果溢出左边
124
+ if (left < marginSize) {
125
+ left = marginSize;
126
+ }
127
+ // 偏移
128
+ top -= parentWrapperOffsetTop;
129
+ left -= parentWrapperOffsetLeft;
130
+ } else {
131
+ if (placement === 'top') {
132
+ panelPlacement = 'top';
133
+ bottom = targetHeight;
134
+ } else if (!placement) {
135
+ // 如果下面不够放,则向上
136
+ top = targetHeight;
137
+ if (targetTop + targetHeight + panelHeight + marginSize > visibleHeight) {
138
+ // 如果上面不够放,则向下(优先)
139
+ if (targetTop - targetHeight - panelHeight > marginSize) {
140
+ panelPlacement = 'top';
141
+ top = '';
142
+ bottom = targetHeight;
143
+ }
144
+ }
145
+ }
146
+ // 是否超出右侧
147
+ if (panelRect.left + panelRect.width + marginSize > visibleWidth) {
148
+ left = -(panelRect.left + panelRect.width + marginSize - visibleWidth);
149
+ }
150
+ }
151
+ if (_xeUtils.default.isNumber(top)) {
152
+ stys.top = top + 'px';
153
+ }
154
+ if (_xeUtils.default.isNumber(bottom)) {
155
+ stys.bottom = bottom + 'px';
156
+ }
157
+ if (_xeUtils.default.isNumber(left)) {
158
+ stys.left = left + 'px';
159
+ }
160
+ if (isMinWidth && _xeUtils.default.isNumber(minWidth)) {
161
+ stys.minWidth = minWidth + 'px';
162
+ }
163
+ // 箭头
164
+ if (left === targetLeft) {
165
+ if (targetWidth <= panelWidth) {
166
+ arrowLeft = targetWidth / 2;
167
+ }
168
+ } else if (left < targetLeft) {
169
+ if (left + panelWidth > targetLeft + targetWidth) {
170
+ arrowLeft = targetLeft - left + targetWidth / 2;
171
+ } else {
172
+ arrowLeft = targetLeft - left + (panelWidth - (targetLeft - left)) / 2;
173
+ }
174
+ }
175
+ }
176
+ }
177
+ return {
178
+ top: top || 0,
179
+ bottom: bottom || 0,
180
+ left: left || 0,
181
+ arrowLeft: arrowLeft || 0,
182
+ style: stys,
183
+ placement: panelPlacement
184
+ };
185
+ },
186
+ /**
187
+ * 获取包裹容器的元素
188
+ */
189
+ getWrapperElement,
190
+ /**
191
+ * 获取插入到指定容器的元素或字符串
192
+ */
193
+ getAppendSelector,
194
+ /**
195
+ * 获取插入到指定容器的元素
196
+ */
197
+ getAppendElement
198
+ };
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.popperUtils=void 0;var _xeUtils=_interopRequireDefault(require("xe-utils"));function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}function getWrapperElement(e){var t;return e&&(t=document.body,(e=e.parentElement)===t?document.documentElement:e)||null}function getAppendSelector(e){return(e&&_xeUtils.default.isFunction(e)?e({}):e)||"body"}function getAppendElement(e){e=getAppendSelector(e);return _xeUtils.default.isString(e)?document.querySelector(e)||document.body:e}let popperUtils=exports.popperUtils={updatePlacement(e,t,o){var{defaultTop:o,defaultLeft:l,placement:n,defaultPlacement:p,appendTo:i,teleportTo:r,marginSize:d,isMinWidth:u}=Object.assign({appendTo:"",teleportTo:!1,marginSize:18,isMinWidth:!0},o);let f="bottom",a="",s="",m=0;var c="";let g="";var b,x,_,h,W,E,U,A,S,v,y,T={};return t&&e&&(E=document.body,i=getWrapperElement(r?getAppendElement(i):t))&&(b=e.offsetWidth,x=e.offsetHeight,_=t.offsetWidth,h=t.offsetHeight,S=i.getBoundingClientRect(),t=t.getBoundingClientRect(),y=e.getBoundingClientRect(),W=E.clientHeight,E=E.clientWidth,U=i.scrollLeft,i=i.scrollTop,A=S.left,S=S.top,v=y.left,y=y.top,c=e.offsetWidth,r?(m=(l||v-(_-b)/2)-U,a=(o||y+x)-i,"top"===n?(f="top",a=y-h):n||("top"===p?(f="top",(a=o?a:y-h)<d&&(f="bottom",a=y+x),a+h+d>W&&(f="top",a=y-h)):(a+h+d>W&&(f="top",a=y-h),a<d&&(f="bottom",a=y+x))),m+_+d>E&&(m-=m+_+d-E),m<d&&(m=d),a-=S,m-=A):("top"===n?(f="top",s=x):n||W<y+(a=x)+h+d&&d<y-x-h&&(f="top",a="",s=x),t.left+t.width+d>E&&(m=-(t.left+t.width+d-E))),_xeUtils.default.isNumber(a)&&(T.top=a+"px"),_xeUtils.default.isNumber(s)&&(T.bottom=s+"px"),_xeUtils.default.isNumber(m)&&(T.left=m+"px"),u&&_xeUtils.default.isNumber(c)&&(T.minWidth=c+"px"),m===v?b<=_&&(g=b/2):m<v&&(g=m+_>v+b?v-m+b/2:v-m+(_-(v-m))/2)),{top:a||0,bottom:s||0,left:m||0,arrowLeft:g||0,style:T,placement:f}},getWrapperElement:getWrapperElement,getAppendSelector:getAppendSelector,getAppendElement:getAppendElement};
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.scrollbarUtils = void 0;
7
+ const scrollbarUtils = exports.scrollbarUtils = {
8
+ /**
9
+ * 计算滚动条滑块的长度
10
+ */
11
+ getThumbSize(viewportSize, contentSize, trackSize) {
12
+ if (contentSize <= viewportSize) {
13
+ return 0;
14
+ }
15
+ const ratio = viewportSize / contentSize;
16
+ const ideal = trackSize * ratio;
17
+ return Math.max(20, Math.min(ideal, trackSize));
18
+ },
19
+ /**
20
+ * 计算滚动条滑块的偏移位置
21
+ */
22
+ getThumbOffsetSize(offsetSize, viewportSize, contentSize, trackSize, thumbSize) {
23
+ const maxScroll = contentSize - viewportSize;
24
+ if (maxScroll <= 0) {
25
+ return 0;
26
+ }
27
+ const maxThumbOffset = trackSize - thumbSize;
28
+ if (maxThumbOffset <= 0) {
29
+ return 0;
30
+ }
31
+ const clamped = Math.min(Math.max(offsetSize, 0), maxScroll);
32
+ const ratio = clamped / maxScroll;
33
+ return ratio * maxThumbOffset;
34
+ },
35
+ /**
36
+ * 根据点击轨道的位置,计算滑块的偏移位置
37
+ */
38
+ getScrollFromClick(clickPosition, viewportSize, contentSize, trackSize, thumbSize) {
39
+ const maxThumbOffset = trackSize - thumbSize;
40
+ const maxScroll = contentSize - viewportSize;
41
+ if (maxScroll <= 0 || maxThumbOffset <= 0) {
42
+ return 0;
43
+ }
44
+ let thumbOffset = clickPosition - thumbSize / 2;
45
+ thumbOffset = Math.min(Math.max(thumbOffset, 0), maxThumbOffset);
46
+ const scrollOffset = thumbOffset / maxThumbOffset * maxScroll;
47
+ return scrollOffset;
48
+ },
49
+ /**
50
+ * 拖拽 thumb 时,根据鼠标位置计算滑块的偏移位置
51
+ */
52
+ getScrollFromDrag(pointerPosition, dragOffset, viewportSize, contentSize, trackSize, thumbSize) {
53
+ const maxScroll = contentSize - viewportSize;
54
+ const maxThumbOffset = trackSize - thumbSize;
55
+ if (maxScroll <= 0 || maxThumbOffset <= 0) {
56
+ return 0;
57
+ }
58
+ let thumbOffset = pointerPosition - dragOffset;
59
+ thumbOffset = Math.min(Math.max(thumbOffset, 0), maxThumbOffset);
60
+ return thumbOffset / maxThumbOffset * maxScroll;
61
+ }
62
+ };
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,"__esModule",{value:!0}),exports.scrollbarUtils=void 0;let scrollbarUtils=exports.scrollbarUtils={getThumbSize(t,r,e){return r<=t?0:Math.max(20,Math.min(e*(t/r),e))},getThumbOffsetSize(t,r,e,a,l){e-=r;return e<=0||(r=a-l)<=0?0:Math.min(Math.max(t,0),e)/e*r},getScrollFromClick(t,r,e,a,l){a-=l,e-=r;return e<=0||a<=0?0:(r=t-l/2,(r=Math.min(Math.max(r,0),a))/a*e)},getScrollFromDrag(t,r,e,a,l,o){a-=e,e=l-o;return a<=0||e<=0?0:(l=t-r,(l=Math.min(Math.max(l,0),e))/e*a)}};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vxe-ui/core",
3
- "version": "4.4.30",
3
+ "version": "4.4.32",
4
4
  "description": "Vxe UI core library",
5
5
  "scripts": {
6
6
  "update": "npm install --legacy-peer-deps",
package/packages/index.ts CHANGED
@@ -17,6 +17,8 @@ import { clipboard } from './src/clipboard'
17
17
  import { permission } from './src/permission'
18
18
  import { baseUtils } from './src/base-utils'
19
19
  import { domUtils } from './src/dom-utils'
20
+ import { scrollbarUtils } from './src/scrollbar-utils'
21
+ import { popperUtils } from './src/popper-utils'
20
22
  import { log } from './src/log'
21
23
  import { hooks } from './src/hooks'
22
24
  import { useFns } from './src/useFns'
@@ -109,6 +111,8 @@ export const VxeUI = Object.assign(VxeCore, {
109
111
 
110
112
  baseUtils,
111
113
  domUtils,
114
+ scrollbarUtils,
115
+ popperUtils,
112
116
 
113
117
  globalStore,
114
118
 
@@ -146,6 +150,8 @@ export * from './src/permission'
146
150
 
147
151
  export * from './src/base-utils'
148
152
  export * from './src/dom-utils'
153
+ export * from './src/scrollbar-utils'
154
+ export * from './src/popper-utils'
149
155
 
150
156
  export * from './src/dataStore'
151
157
 
@@ -0,0 +1,194 @@
1
+ import XEUtils from 'xe-utils'
2
+
3
+ import type { VxePopperUtils, VxePopperOptions } from '../../types'
4
+
5
+ /**
6
+ * 获取包裹容器的元素
7
+ */
8
+ function getWrapperElement (panelElem: HTMLElement | null | undefined) {
9
+ if (!panelElem) {
10
+ return null
11
+ }
12
+ const bodyEl = document.body
13
+ const parentEl = panelElem.parentElement
14
+ return (parentEl === bodyEl ? document.documentElement : parentEl) || null
15
+ }
16
+
17
+ /**
18
+ * 获取插入到指定容器的元素或字符串
19
+ */
20
+ function getAppendSelector (appendTo: string | HTMLElement | ((params: any) => string | HTMLElement) | undefined) {
21
+ const selectElem = appendTo && XEUtils.isFunction(appendTo) ? appendTo({}) : appendTo
22
+ return selectElem || 'body'
23
+ }
24
+
25
+ /**
26
+ * 获取插入到指定容器的元素
27
+ */
28
+ function getAppendElement (appendTo: string | HTMLElement | ((params: any) => string | HTMLElement) | undefined) {
29
+ const selectElem = getAppendSelector(appendTo)
30
+ if (XEUtils.isString(selectElem)) {
31
+ return document.querySelector<HTMLElement>(selectElem) || document.body
32
+ }
33
+ return selectElem
34
+ }
35
+
36
+ export const popperUtils: VxePopperUtils = {
37
+ /**
38
+ * 更新位置
39
+ */
40
+ updatePlacement (targetElem: HTMLElement | null | undefined, panelElem: HTMLElement | null | undefined, options: VxePopperOptions) {
41
+ const { defaultTop, defaultLeft, placement, defaultPlacement, appendTo, teleportTo, marginSize, isMinWidth } = Object.assign({
42
+ appendTo: '',
43
+ teleportTo: false,
44
+ marginSize: 18,
45
+ isMinWidth: true
46
+ }, options)
47
+ let panelPlacement: 'top' | 'bottom' = 'bottom'
48
+ let top: number | '' = ''
49
+ let bottom: number | '' = ''
50
+ let left = 0
51
+ let minWidth: number | '' = ''
52
+ let arrowLeft: number | '' = ''
53
+ const stys: Record<string, string> = {}
54
+ if (panelElem && targetElem) {
55
+ const bodyEl = document.body
56
+ const parentWrapperEl = getWrapperElement(teleportTo ? getAppendElement(appendTo) : panelElem)
57
+ if (parentWrapperEl) {
58
+ const targetWidth = targetElem.offsetWidth
59
+ const targetHeight = targetElem.offsetHeight
60
+ const panelWidth = panelElem.offsetWidth
61
+ const panelHeight = panelElem.offsetHeight
62
+
63
+ const parentWrapperRect = parentWrapperEl.getBoundingClientRect()
64
+ const panelRect = panelElem.getBoundingClientRect()
65
+ const targetRect = targetElem.getBoundingClientRect()
66
+ const visibleHeight = bodyEl.clientHeight
67
+ const visibleWidth = bodyEl.clientWidth
68
+
69
+ const parentWrapperScrollLeft = parentWrapperEl.scrollLeft
70
+ const parentWrapperScrollTop = parentWrapperEl.scrollTop
71
+ const parentWrapperOffsetLeft = parentWrapperRect.left
72
+ const parentWrapperOffsetTop = parentWrapperRect.top
73
+ const targetLeft = targetRect.left
74
+ const targetTop = targetRect.top
75
+
76
+ minWidth = targetElem.offsetWidth
77
+
78
+ if (teleportTo) {
79
+ left = (defaultLeft || (targetLeft - (panelWidth - targetWidth) / 2)) - parentWrapperScrollLeft
80
+ top = (defaultTop || (targetTop + targetHeight)) - parentWrapperScrollTop
81
+ if (placement === 'top') {
82
+ panelPlacement = 'top'
83
+ top = targetTop - panelHeight
84
+ } else if (!placement) {
85
+ if (defaultPlacement === 'top') {
86
+ panelPlacement = 'top'
87
+ if (!defaultTop) {
88
+ top = targetTop - panelHeight
89
+ }
90
+ // 如果上面不够放,则向下
91
+ if (top < marginSize) {
92
+ panelPlacement = 'bottom'
93
+ top = targetTop + targetHeight
94
+ }
95
+ // 如果下面不够放,则向上(优先)
96
+ if (top + panelHeight + marginSize > visibleHeight) {
97
+ panelPlacement = 'top'
98
+ top = targetTop - panelHeight
99
+ }
100
+ } else {
101
+ // 如果下面不够放,则向上
102
+ if (top + panelHeight + marginSize > visibleHeight) {
103
+ panelPlacement = 'top'
104
+ top = targetTop - panelHeight
105
+ }
106
+ // 如果上面不够放,则向下(优先)
107
+ if (top < marginSize) {
108
+ panelPlacement = 'bottom'
109
+ top = targetTop + targetHeight
110
+ }
111
+ }
112
+ }
113
+ // 如果溢出右边
114
+ if (left + panelWidth + marginSize > visibleWidth) {
115
+ left -= left + panelWidth + marginSize - visibleWidth
116
+ }
117
+ // 如果溢出左边
118
+ if (left < marginSize) {
119
+ left = marginSize
120
+ }
121
+
122
+ // 偏移
123
+ top -= parentWrapperOffsetTop
124
+ left -= parentWrapperOffsetLeft
125
+ } else {
126
+ if (placement === 'top') {
127
+ panelPlacement = 'top'
128
+ bottom = targetHeight
129
+ } else if (!placement) {
130
+ // 如果下面不够放,则向上
131
+ top = targetHeight
132
+ if (targetTop + targetHeight + panelHeight + marginSize > visibleHeight) {
133
+ // 如果上面不够放,则向下(优先)
134
+ if (targetTop - targetHeight - panelHeight > marginSize) {
135
+ panelPlacement = 'top'
136
+ top = ''
137
+ bottom = targetHeight
138
+ }
139
+ }
140
+ }
141
+ // 是否超出右侧
142
+ if (panelRect.left + panelRect.width + marginSize > visibleWidth) {
143
+ left = -(panelRect.left + panelRect.width + marginSize - visibleWidth)
144
+ }
145
+ }
146
+ if (XEUtils.isNumber(top)) {
147
+ stys.top = top + 'px'
148
+ }
149
+ if (XEUtils.isNumber(bottom)) {
150
+ stys.bottom = bottom + 'px'
151
+ }
152
+ if (XEUtils.isNumber(left)) {
153
+ stys.left = left + 'px'
154
+ }
155
+ if (isMinWidth && XEUtils.isNumber(minWidth)) {
156
+ stys.minWidth = minWidth + 'px'
157
+ }
158
+
159
+ // 箭头
160
+ if (left === targetLeft) {
161
+ if (targetWidth <= panelWidth) {
162
+ arrowLeft = targetWidth / 2
163
+ }
164
+ } else if (left < targetLeft) {
165
+ if (left + panelWidth > targetLeft + targetWidth) {
166
+ arrowLeft = (targetLeft - left) + targetWidth / 2
167
+ } else {
168
+ arrowLeft = (targetLeft - left) + (panelWidth - (targetLeft - left)) / 2
169
+ }
170
+ }
171
+ }
172
+ }
173
+ return {
174
+ top: top || 0,
175
+ bottom: bottom || 0,
176
+ left: left || 0,
177
+ arrowLeft: arrowLeft || 0,
178
+ style: stys,
179
+ placement: panelPlacement
180
+ }
181
+ },
182
+ /**
183
+ * 获取包裹容器的元素
184
+ */
185
+ getWrapperElement,
186
+ /**
187
+ * 获取插入到指定容器的元素或字符串
188
+ */
189
+ getAppendSelector,
190
+ /**
191
+ * 获取插入到指定容器的元素
192
+ */
193
+ getAppendElement
194
+ }
@@ -0,0 +1,58 @@
1
+ import { VxeScrollbarUtils } from '../../types'
2
+
3
+ export const scrollbarUtils: VxeScrollbarUtils = {
4
+ /**
5
+ * 计算滚动条滑块的长度
6
+ */
7
+ getThumbSize (viewportSize: number, contentSize: number, trackSize: number) {
8
+ if (contentSize <= viewportSize) {
9
+ return 0
10
+ }
11
+ const ratio = viewportSize / contentSize
12
+ const ideal = trackSize * ratio
13
+ return Math.max(20, Math.min(ideal, trackSize))
14
+ },
15
+ /**
16
+ * 计算滚动条滑块的偏移位置
17
+ */
18
+ getThumbOffsetSize (offsetSize: number, viewportSize: number, contentSize: number, trackSize: number, thumbSize: number) {
19
+ const maxScroll = contentSize - viewportSize
20
+ if (maxScroll <= 0) {
21
+ return 0
22
+ }
23
+ const maxThumbOffset = trackSize - thumbSize
24
+ if (maxThumbOffset <= 0) {
25
+ return 0
26
+ }
27
+ const clamped = Math.min(Math.max(offsetSize, 0), maxScroll)
28
+ const ratio = clamped / maxScroll
29
+ return ratio * maxThumbOffset
30
+ },
31
+ /**
32
+ * 根据点击轨道的位置,计算滑块的偏移位置
33
+ */
34
+ getScrollFromClick (clickPosition: number, viewportSize: number, contentSize: number, trackSize: number, thumbSize: number) {
35
+ const maxThumbOffset = trackSize - thumbSize
36
+ const maxScroll = contentSize - viewportSize
37
+ if (maxScroll <= 0 || maxThumbOffset <= 0) {
38
+ return 0
39
+ }
40
+ let thumbOffset = clickPosition - thumbSize / 2
41
+ thumbOffset = Math.min(Math.max(thumbOffset, 0), maxThumbOffset)
42
+ const scrollOffset = (thumbOffset / maxThumbOffset) * maxScroll
43
+ return scrollOffset
44
+ },
45
+ /**
46
+ * 拖拽 thumb 时,根据鼠标位置计算滑块的偏移位置
47
+ */
48
+ getScrollFromDrag (pointerPosition: number, dragOffset: number, viewportSize: number, contentSize: number, trackSize: number, thumbSize: number) {
49
+ const maxScroll = contentSize - viewportSize
50
+ const maxThumbOffset = trackSize - thumbSize
51
+ if (maxScroll <= 0 || maxThumbOffset <= 0) {
52
+ return 0
53
+ }
54
+ let thumbOffset = pointerPosition - dragOffset
55
+ thumbOffset = Math.min(Math.max(thumbOffset, 0), maxThumbOffset)
56
+ return (thumbOffset / maxThumbOffset) * maxScroll
57
+ }
58
+ }
@@ -16,6 +16,8 @@ import { VxeGlobalClipboard } from './clipboard'
16
16
  import { VxeGlobalPermission } from './permission'
17
17
  import { VxeBaseUtils } from './base-utils'
18
18
  import { VxeDomUtils } from './dom-utils'
19
+ import { VxeScrollbarUtils } from './scrollbar-utils'
20
+ import { VxePopperUtils } from './popper-utils'
19
21
  import { VxeGlobalComponentMethod, VxeGlobalGetComponentMethod, VxeGlobalHasComponentMethod } from './components'
20
22
  import { VxeGlobalUseFns } from './useFn'
21
23
  import { VxeGlobalHooks } from './hooks'
@@ -74,6 +76,8 @@ export const permission: VxeGlobalPermission
74
76
 
75
77
  export const baseUtils: VxeBaseUtils
76
78
  export const domUtils: VxeDomUtils
79
+ export const scrollbarUtils: VxeScrollbarUtils
80
+ export const popperUtils: VxePopperUtils
77
81
 
78
82
  export const globalEvents: VxeGlobalEvents
79
83
 
@@ -217,6 +221,14 @@ export interface VxeUIExport {
217
221
  * Document 工具函数
218
222
  */
219
223
  domUtils: VxeDomUtils
224
+ /**
225
+ * 滚动条工具函数
226
+ */
227
+ scrollbarUtils: VxeScrollbarUtils
228
+ /**
229
+ * 弹出元素定位工具函数
230
+ */
231
+ popperUtils: VxePopperUtils
220
232
 
221
233
  /**
222
234
  * 全局事件管理
@@ -302,6 +314,8 @@ export * from './permission'
302
314
 
303
315
  export * from './base-utils'
304
316
  export * from './dom-utils'
317
+ export * from './scrollbar-utils'
318
+ export * from './popper-utils'
305
319
 
306
320
  export * from './log'
307
321
 
@@ -0,0 +1,44 @@
1
+ export interface VxePopperOptions {
2
+ defaultTop?: number
3
+ defaultLeft?: number
4
+ placement?: '' | 'top' | 'bottom' | null
5
+ defaultPlacement?: '' | 'top' | 'bottom' | null
6
+ appendTo?: string | HTMLElement | ((params: any) => string | HTMLElement)
7
+ teleportTo?: boolean
8
+ marginSize?: number
9
+ isMinWidth?: boolean
10
+ }
11
+
12
+ export interface VxePopperPlacementResult {
13
+ top: number
14
+ bottom: number
15
+ left: number
16
+ arrowLeft: number
17
+ style: Record<string, string>
18
+ placement: 'top' | 'bottom'
19
+ }
20
+
21
+ export interface VxePopperUtils {
22
+ /**
23
+ * 更新位置
24
+ * @param targetElem
25
+ * @param panelElem
26
+ * @param options
27
+ */
28
+ updatePlacement (targetElem: HTMLElement | null | undefined, panelElem: HTMLElement | null | undefined, options: VxePopperOptions): VxePopperPlacementResult
29
+ /**
30
+ * 获取包裹容器的元素
31
+ * @param panelElem
32
+ */
33
+ getWrapperElement (panelElem: HTMLElement | null | undefined): HTMLElement | null
34
+ /**
35
+ * 获取插入到指定容器的元素或字符串
36
+ * @param appendTo
37
+ */
38
+ getAppendSelector (appendTo: string | HTMLElement | ((params: any) => string | HTMLElement) | undefined): HTMLElement | string
39
+ /**
40
+ * 获取插入到指定容器的元素
41
+ * @param appendTo
42
+ */
43
+ getAppendElement (appendTo: string | HTMLElement | ((params: any) => string | HTMLElement) | undefined): HTMLElement | null
44
+ }
@@ -0,0 +1,38 @@
1
+
2
+ export interface VxeScrollbarUtils {
3
+ /**
4
+ * 计算滚动条滑块的长度
5
+ * @param viewportSize
6
+ * @param contentSize
7
+ * @param trackSize
8
+ */
9
+ getThumbSize (viewportSize: number, contentSize: number, trackSize: number): number
10
+ /**
11
+ * 计算滚动条滑块的偏移位置
12
+ * @param offsetSize
13
+ * @param viewportSize
14
+ * @param contentSize
15
+ * @param trackSize
16
+ * @param thumbSize
17
+ */
18
+ getThumbOffsetSize (offsetSize: number, viewportSize: number, contentSize: number, trackSize: number, thumbSize: number): number
19
+ /**
20
+ * 根据点击轨道的位置,计算滑块的偏移位置
21
+ * @param clickPosition
22
+ * @param viewportSize
23
+ * @param contentSize
24
+ * @param trackSize
25
+ * @param thumbSize
26
+ */
27
+ getScrollFromClick (clickPosition: number, viewportSize: number, contentSize: number, trackSize: number, thumbSize: number): number
28
+ /**
29
+ * 拖拽 thumb 时,根据鼠标位置计算滑块的偏移位置
30
+ * @param pointerPosition
31
+ * @param dragOffset
32
+ * @param viewportSize
33
+ * @param contentSize
34
+ * @param trackSize
35
+ * @param thumbSize
36
+ */
37
+ getScrollFromDrag (pointerPosition: number, dragOffset: number, viewportSize: number, contentSize: number, trackSize: number, thumbSize: number): number
38
+ }