1
- import { CSSProperties , defineComponent , inject } from 'vue' ;
1
+ import {
2
+ CSSProperties ,
3
+ defineComponent ,
4
+ inject ,
5
+ ref ,
6
+ reactive ,
7
+ watch ,
8
+ onMounted ,
9
+ getCurrentInstance ,
10
+ onUnmounted ,
11
+ } from 'vue' ;
2
12
import PropTypes from '../_util/vue-types' ;
3
13
import classNames from '../_util/classNames' ;
4
14
import omit from 'omit.js' ;
5
15
import ResizeObserver from '../vc-resize-observer' ;
6
- import BaseMixin from '../_util/BaseMixin' ;
16
+ // import BaseMixin from '../_util/BaseMixin';
7
17
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame' ;
8
18
import { defaultConfigProvider } from '../config-provider' ;
9
19
import warning from '../_util/warning' ;
@@ -50,81 +60,29 @@ const AffixProps = {
50
60
} ;
51
61
const Affix = defineComponent ( {
52
62
name : 'AAffix' ,
53
- mixins : [ BaseMixin ] ,
54
63
props : AffixProps ,
55
64
emits : [ 'change' , 'testUpdatePosition' ] ,
56
- setup ( ) {
57
- return {
58
- configProvider : inject ( 'configProvider' , defaultConfigProvider ) ,
59
- } ;
60
- } ,
61
- data ( ) {
62
- return {
65
+ setup ( props , { slots, emit, expose } ) {
66
+ const configProvider = inject ( 'configProvider' , defaultConfigProvider ) ;
67
+ const placeholderNode = ref ( ) ;
68
+ const fixedNode = ref ( ) ;
69
+ const state = reactive ( {
63
70
affixStyle : undefined ,
64
71
placeholderStyle : undefined ,
65
72
status : AffixStatus . None ,
66
73
lastAffix : false ,
67
74
prevTarget : null ,
68
75
timeout : null ,
69
- } ;
70
- } ,
71
- watch : {
72
- target ( val ) {
73
- let newTarget = null ;
74
- if ( val ) {
75
- newTarget = val ( ) || null ;
76
- }
77
- if ( this . prevTarget !== newTarget ) {
78
- removeObserveTarget ( this ) ;
79
- if ( newTarget ) {
80
- addObserveTarget ( newTarget , this ) ;
81
- // Mock Event object.
82
- this . updatePosition ( ) ;
83
- }
84
- this . prevTarget = newTarget ;
85
- }
86
- } ,
87
- offsetTop ( ) {
88
- this . updatePosition ( ) ;
89
- } ,
90
- offsetBottom ( ) {
91
- this . updatePosition ( ) ;
92
- } ,
93
- } ,
94
- beforeMount ( ) {
95
- this . updatePosition = throttleByAnimationFrame ( this . updatePosition ) ;
96
- this . lazyUpdatePosition = throttleByAnimationFrame ( this . lazyUpdatePosition ) ;
97
- } ,
98
- mounted ( ) {
99
- const { target } = this ;
100
- if ( target ) {
101
- // [Legacy] Wait for parent component ref has its value.
102
- // We should use target as directly element instead of function which makes element check hard.
103
- this . timeout = setTimeout ( ( ) => {
104
- addObserveTarget ( target ( ) , this ) ;
105
- // Mock Event object.
106
- this . updatePosition ( ) ;
107
- } ) ;
108
- }
109
- } ,
110
- updated ( ) {
111
- this . measure ( ) ;
112
- } ,
113
- beforeUnmount ( ) {
114
- clearTimeout ( this . timeout ) ;
115
- removeObserveTarget ( this ) ;
116
- ( this . updatePosition as any ) . cancel ( ) ;
117
- // https://github.com/ant-design/ant-design/issues/22683
118
- ( this . lazyUpdatePosition as any ) . cancel ( ) ;
119
- } ,
120
- methods : {
121
- getOffsetTop ( ) {
122
- const { offset, offsetBottom } = this ;
123
- let { offsetTop } = this ;
124
- if ( typeof offsetTop === 'undefined' ) {
76
+ } ) ;
77
+ const currentInstance = getCurrentInstance ( ) ;
78
+
79
+ const getOffsetTop = ( ) => {
80
+ const { offset, offsetBottom } = props ;
81
+ let { offsetTop } = props ;
82
+ if ( offsetTop === undefined ) {
125
83
offsetTop = offset ;
126
84
warning (
127
- typeof offset === ' undefined' ,
85
+ offset === undefined ,
128
86
'Affix' ,
129
87
'`offset` is deprecated. Please use `offsetTop` instead.' ,
130
88
) ;
@@ -134,26 +92,19 @@ const Affix = defineComponent({
134
92
offsetTop = 0 ;
135
93
}
136
94
return offsetTop ;
137
- } ,
138
-
139
- getOffsetBottom ( ) {
140
- return this . offsetBottom ;
141
- } ,
142
- // =================== Measure ===================
143
- measure ( ) {
144
- const { status, lastAffix } = this ;
145
- const { target } = this ;
146
- if (
147
- status !== AffixStatus . Prepare ||
148
- ! this . $refs . fixedNode ||
149
- ! this . $refs . placeholderNode ||
150
- ! target
151
- ) {
95
+ } ;
96
+ const getOffsetBottom = ( ) => {
97
+ return props . offsetBottom ;
98
+ } ;
99
+ const measure = ( ) => {
100
+ const { status, lastAffix } = state ;
101
+ const { target } = props ;
102
+ if ( status !== AffixStatus . Prepare || ! fixedNode . value || ! placeholderNode . value || ! target ) {
152
103
return ;
153
104
}
154
105
155
- const offsetTop = this . getOffsetTop ( ) ;
156
- const offsetBottom = this . getOffsetBottom ( ) ;
106
+ const offsetTop = getOffsetTop ( ) ;
107
+ const offsetBottom = getOffsetBottom ( ) ;
157
108
158
109
const targetNode = target ( ) ;
159
110
if ( ! targetNode ) {
@@ -164,7 +115,7 @@ const Affix = defineComponent({
164
115
status : AffixStatus . None ,
165
116
} as AffixState ;
166
117
const targetRect = getTargetRect ( targetNode ) ;
167
- const placeholderReact = getTargetRect ( this . $refs . placeholderNode as HTMLElement ) ;
118
+ const placeholderReact = getTargetRect ( placeholderNode . value as HTMLElement ) ;
168
119
const fixedTop = getFixedTop ( placeholderReact , targetRect , offsetTop ) ;
169
120
const fixedBottom = getFixedBottom ( placeholderReact , targetRect , offsetBottom ) ;
170
121
if ( fixedTop !== undefined ) {
@@ -193,41 +144,39 @@ const Affix = defineComponent({
193
144
194
145
newState . lastAffix = ! ! newState . affixStyle ;
195
146
if ( lastAffix !== newState . lastAffix ) {
196
- this . $ emit( 'change' , newState . lastAffix ) ;
147
+ emit ( 'change' , newState . lastAffix ) ;
197
148
}
198
-
199
- this . setState ( newState ) ;
200
- } ,
201
-
202
- prepareMeasure ( ) {
203
- this . setState ( {
149
+ // update state
150
+ Object . assign ( state , newState ) ;
151
+ } ;
152
+ const prepareMeasure = ( ) => {
153
+ Object . assign ( state , {
204
154
status : AffixStatus . Prepare ,
205
155
affixStyle : undefined ,
206
156
placeholderStyle : undefined ,
207
157
} ) ;
208
- this . $forceUpdate ( ) ;
209
-
210
158
// Test if `updatePosition` called
211
159
if ( process . env . NODE_ENV === 'test' ) {
212
- this . $ emit( 'testUpdatePosition' ) ;
160
+ emit ( 'testUpdatePosition' ) ;
213
161
}
214
- } ,
215
- updatePosition ( ) {
216
- this . prepareMeasure ( ) ;
217
- } ,
218
- lazyUpdatePosition ( ) {
219
- const { target } = this ;
220
- const { affixStyle } = this ;
162
+ } ;
163
+
164
+ const updatePosition = throttleByAnimationFrame ( ( ) => {
165
+ prepareMeasure ( ) ;
166
+ } ) ;
167
+ const lazyUpdatePosition = throttleByAnimationFrame ( ( ) => {
168
+ const { target } = props ;
169
+ const { affixStyle } = state ;
221
170
222
171
// Check position change before measure to make Safari smooth
223
172
if ( target && affixStyle ) {
224
- const offsetTop = this . getOffsetTop ( ) ;
225
- const offsetBottom = this . getOffsetBottom ( ) ;
173
+ const offsetTop = getOffsetTop ( ) ;
174
+ const offsetBottom = getOffsetBottom ( ) ;
226
175
227
176
const targetNode = target ( ) ;
228
- if ( targetNode && this . $refs . placeholderNode ) {
177
+ if ( targetNode && placeholderNode . value ) {
229
178
const targetRect = getTargetRect ( targetNode ) ;
230
- const placeholderReact = getTargetRect ( this . $refs . placeholderNode as HTMLElement ) ;
179
+ const placeholderReact = getTargetRect ( placeholderNode . value as HTMLElement ) ;
231
180
const fixedTop = getFixedTop ( placeholderReact , targetRect , offsetTop ) ;
232
181
const fixedBottom = getFixedBottom ( placeholderReact , targetRect , offsetBottom ) ;
233
182
@@ -240,30 +189,78 @@ const Affix = defineComponent({
240
189
}
241
190
}
242
191
// Directly call prepare measure since it's already throttled.
243
- this . prepareMeasure ( ) ;
244
- } ,
245
- } ,
192
+ prepareMeasure ( ) ;
193
+ } ) ;
246
194
247
- render ( ) {
248
- const { prefixCls, affixStyle, placeholderStyle, $slots, $props } = this ;
249
- const getPrefixCls = this . configProvider . getPrefixCls ;
250
- const className = classNames ( {
251
- [ getPrefixCls ( 'affix' , prefixCls ) ] : affixStyle ,
195
+ expose ( {
196
+ updatePosition,
197
+ lazyUpdatePosition,
252
198
} ) ;
253
- const props = omit ( $props , [ 'prefixCls' , 'offsetTop' , 'offsetBottom' , 'target' ] ) ;
254
- return (
255
- < ResizeObserver
256
- onResize = { ( ) => {
257
- this . updatePosition ( ) ;
258
- } }
259
- >
260
- < div { ...props } style = { placeholderStyle } ref = "placeholderNode" >
261
- < div class = { className } ref = "fixedNode" style = { affixStyle } >
262
- { $slots . default ?.( ) }
263
- </ div >
264
- </ div >
265
- </ ResizeObserver >
199
+ watch (
200
+ ( ) => props . target ,
201
+ val => {
202
+ let newTarget = null ;
203
+ if ( val ) {
204
+ newTarget = val ( ) || null ;
205
+ }
206
+ if ( state . prevTarget !== newTarget ) {
207
+ removeObserveTarget ( currentInstance ) ;
208
+ if ( newTarget ) {
209
+ addObserveTarget ( newTarget , currentInstance ) ;
210
+ // Mock Event object.
211
+ updatePosition ( ) ;
212
+ }
213
+ state . prevTarget = newTarget ;
214
+ }
215
+ } ,
266
216
) ;
217
+ watch ( ( ) => [ props . offsetTop , props . offsetBottom ] , updatePosition ) ;
218
+ watch (
219
+ ( ) => state . status ,
220
+ ( ) => {
221
+ measure ( ) ;
222
+ } ,
223
+ ) ;
224
+
225
+ onMounted ( ( ) => {
226
+ const { target } = props ;
227
+ if ( target ) {
228
+ // [Legacy] Wait for parent component ref has its value.
229
+ // We should use target as directly element instead of function which makes element check hard.
230
+ state . timeout = setTimeout ( ( ) => {
231
+ addObserveTarget ( target ( ) , currentInstance ) ;
232
+ // Mock Event object.
233
+ updatePosition ( ) ;
234
+ } ) ;
235
+ }
236
+ } ) ;
237
+
238
+ onUnmounted ( ( ) => {
239
+ clearTimeout ( state . timeout ) ;
240
+ removeObserveTarget ( currentInstance ) ;
241
+ ( updatePosition as any ) . cancel ( ) ;
242
+ // https://github.com/ant-design/ant-design/issues/22683
243
+ ( lazyUpdatePosition as any ) . cancel ( ) ;
244
+ } ) ;
245
+
246
+ return ( ) => {
247
+ const { prefixCls } = props ;
248
+ const { affixStyle, placeholderStyle } = state ;
249
+ const { getPrefixCls } = configProvider ;
250
+ const className = classNames ( {
251
+ [ getPrefixCls ( 'affix' , prefixCls ) ] : affixStyle ,
252
+ } ) ;
253
+ const restProps = omit ( props , [ 'prefixCls' , 'offsetTop' , 'offsetBottom' , 'target' ] ) ;
254
+ return (
255
+ < ResizeObserver onResize = { updatePosition } >
256
+ < div { ...restProps } style = { placeholderStyle } ref = { placeholderNode } >
257
+ < div class = { className } ref = { fixedNode } style = { affixStyle } >
258
+ { slots . default ?.( ) }
259
+ </ div >
260
+ </ div >
261
+ </ ResizeObserver >
262
+ ) ;
263
+ } ;
267
264
} ,
268
265
} ) ;
269
266
0 commit comments