33 * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
44 */
55
6- import { AnimationEvent } from '@angular/animations' ;
76import { Directionality } from '@angular/cdk/bidi' ;
87import { DOWN_ARROW , LEFT_ARROW , RIGHT_ARROW , UP_ARROW } from '@angular/cdk/keycodes' ;
98import {
109 afterNextRender ,
1110 booleanAttribute ,
1211 ChangeDetectionStrategy ,
13- ChangeDetectorRef ,
1412 Component ,
1513 computed ,
1614 contentChildren ,
@@ -22,18 +20,20 @@ import {
2220 Input ,
2321 OnChanges ,
2422 Output ,
23+ signal ,
2524 SimpleChanges ,
2625 viewChildren ,
2726 ViewEncapsulation
2827} from '@angular/core' ;
2928import { takeUntilDestroyed } from '@angular/core/rxjs-interop' ;
3029import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ;
31- import { bufferCount , filter } from 'rxjs/operators' ;
30+ import { filter } from 'rxjs/operators' ;
3231
33- import { ThumbAnimationProps , thumbMotion } from 'ng-zorro-antd/core/animation' ;
34- import { NzConfigKey , NzConfigService , WithConfig } from 'ng-zorro-antd/core/config' ;
32+ import { withAnimationCheck } from 'ng-zorro-antd/core/animation' ;
33+ import { NzConfigKey , WithConfig } from 'ng-zorro-antd/core/config' ;
3534import { NzOutletModule } from 'ng-zorro-antd/core/outlet' ;
36- import { NzSizeLDSType , OnChangeType , OnTouchedType } from 'ng-zorro-antd/core/types' ;
35+ import { requestAnimationFrame } from 'ng-zorro-antd/core/polyfill' ;
36+ import { NgStyleInterface , NzSizeLDSType , OnChangeType , OnTouchedType } from 'ng-zorro-antd/core/types' ;
3737import { NzIconModule } from 'ng-zorro-antd/icon' ;
3838
3939import { NzSegmentedItemComponent } from './segmented-item.component' ;
@@ -50,11 +50,12 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'segmented';
5050 template : `
5151 <!-- thumb motion div -->
5252 <div class="ant-segmented-group">
53- @if (animationState ) {
53+ @if (showThumb() ) {
5454 <div
55- class="ant-segmented-thumb ant-segmented-thumb-motion"
56- [@thumbMotion]="animationState"
57- (@thumbMotion.done)="handleThumbAnimationDone($event)"
55+ class="ant-segmented-thumb"
56+ [style]="thumbStyle()"
57+ [animate.enter]="thumbAnimationEnter()"
58+ (transitionend)="handleTransitionEnd($event)"
5859 ></div>
5960 }
6061
@@ -89,14 +90,11 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'segmented';
8990 multi : true
9091 }
9192 ] ,
92- animations : [ thumbMotion ] ,
9393 imports : [ NzIconModule , NzOutletModule , NzSegmentedItemComponent ]
9494} )
9595export class NzSegmentedComponent implements OnChanges , ControlValueAccessor {
9696 readonly _nzModuleName : NzConfigKey = NZ_CONFIG_MODULE_NAME ;
9797
98- public readonly nzConfigService = inject ( NzConfigService ) ;
99- private readonly cdr = inject ( ChangeDetectorRef ) ;
10098 private readonly service = inject ( NzSegmentedService ) ;
10199 private readonly injector = inject ( Injector ) ;
102100 protected readonly dir = inject ( Directionality ) . valueSignal ;
@@ -122,10 +120,9 @@ export class NzSegmentedComponent implements OnChanges, ControlValueAccessor {
122120 private isDisabledFirstChange = true ;
123121
124122 protected value ?: number | string ;
125- protected animationState : null | { value : string ; params : ThumbAnimationProps } = {
126- value : 'to' ,
127- params : thumbAnimationParamsOf ( )
128- } ;
123+ protected readonly thumbStyle = signal < NgStyleInterface | null > ( null ) ;
124+ protected readonly thumbAnimationEnter = withAnimationCheck ( ( ) => 'ant-segmented-thumb-motion-appear-active' ) ;
125+ protected readonly showThumb = this . service . showThumb ;
129126 protected normalizedOptions : NzSegmentedOption [ ] = [ ] ;
130127 protected onChange : OnChangeType = ( ) => { } ;
131128 protected onTouched : OnTouchedType = ( ) => { } ;
@@ -135,37 +132,26 @@ export class NzSegmentedComponent implements OnChanges, ControlValueAccessor {
135132 this . value = value ;
136133 } ) ;
137134
135+ this . service . activated$ . pipe ( takeUntilDestroyed ( ) ) . subscribe ( element => {
136+ this . thumbStyle . update ( prevStyle => {
137+ const nextStyle = this . calcThumbStyle ( element ) ;
138+
139+ if ( prevStyle && nextStyle ) {
140+ // Trigger animation to end position
141+ requestAnimationFrame ( ( ) => {
142+ this . thumbStyle . set ( this . getThumbStyle ( nextStyle ) ) ;
143+ } ) ;
144+ } else if ( nextStyle ) {
145+ return this . getThumbStyle ( nextStyle ) ;
146+ }
147+ return prevStyle ;
148+ } ) ;
149+ } ) ;
150+
138151 this . service . change$ . pipe ( takeUntilDestroyed ( ) ) . subscribe ( value => {
139152 this . nzValueChange . emit ( value ) ;
140153 this . onChange ( value ) ;
141- } ) ;
142-
143- this . service . activated$ . pipe ( bufferCount ( 2 , 1 ) , takeUntilDestroyed ( ) ) . subscribe ( elements => {
144- if ( this . nzVertical ) {
145- this . animationState = {
146- value : 'fromVertical' ,
147- params : thumbAnimationParamsOf ( elements [ 0 ] , true )
148- } ;
149- this . cdr . detectChanges ( ) ;
150-
151- this . animationState = {
152- value : 'toVertical' ,
153- params : thumbAnimationParamsOf ( elements [ 1 ] , true )
154- } ;
155- this . cdr . detectChanges ( ) ;
156- } else {
157- this . animationState = {
158- value : 'from' ,
159- params : thumbAnimationParamsOf ( elements [ 0 ] )
160- } ;
161- this . cdr . detectChanges ( ) ;
162-
163- this . animationState = {
164- value : 'to' ,
165- params : thumbAnimationParamsOf ( elements [ 1 ] )
166- } ;
167- this . cdr . detectChanges ( ) ;
168- }
154+ this . service . animating$ . next ( true ) ;
169155 } ) ;
170156
171157 this . service . keydown$
@@ -209,13 +195,6 @@ export class NzSegmentedComponent implements OnChanges, ControlValueAccessor {
209195 }
210196 }
211197
212- handleThumbAnimationDone ( event : AnimationEvent ) : void {
213- if ( event . toState === 'to' || event . toState === 'toVertical' ) {
214- this . animationState = null ;
215- }
216- this . service . animationDone$ . next ( event ) ;
217- }
218-
219198 private onOffset ( offset : number ) : void {
220199 const items = this . renderedItemCmps ( ) ;
221200 const total = items . length ;
@@ -272,19 +251,71 @@ export class NzSegmentedComponent implements OnChanges, ControlValueAccessor {
272251 this . nzDisabled = ( this . isDisabledFirstChange && this . nzDisabled ) || disabled ;
273252 this . isDisabledFirstChange = false ;
274253 }
275- }
276254
277- function thumbAnimationParamsOf ( element ?: HTMLElement , vertical : boolean = false ) : ThumbAnimationProps {
278- if ( vertical ) {
255+ /************* Thumb Animation *************/
256+
257+ private calcThumbStyle ( element ?: HTMLElement ) : NgStyleInterface | null {
258+ if ( ! element || ! element . offsetParent ) {
259+ return null ;
260+ }
261+
262+ const parentElement = element . parentElement ;
263+ if ( ! parentElement ) {
264+ return null ;
265+ }
266+
267+ const style : NgStyleInterface = {
268+ left : element . offsetLeft ,
269+ right : parentElement . clientWidth - element . clientWidth - element . offsetLeft ,
270+ width : element . clientWidth ,
271+ top : element . offsetTop ,
272+ bottom : parentElement . clientHeight - element . clientHeight - element . offsetTop ,
273+ height : element . clientHeight
274+ } ;
275+
276+ if ( this . nzVertical ) {
277+ return {
278+ left : 0 ,
279+ right : 0 ,
280+ width : 0 ,
281+ top : style . top ,
282+ bottom : style . bottom ,
283+ height : style . height
284+ } ;
285+ }
286+
287+ return {
288+ left : style . left ,
289+ right : style . right ,
290+ width : style . width ,
291+ top : 0 ,
292+ bottom : 0 ,
293+ height : 0
294+ } ;
295+ }
296+
297+ private getThumbStyle ( targetStyle : NgStyleInterface ) : NgStyleInterface {
298+ if ( this . nzVertical ) {
299+ return {
300+ transform : `translateY(${ targetStyle . top } px)` ,
301+ width : '100%' ,
302+ height : `${ targetStyle . height } px`
303+ } ;
304+ }
305+
306+ const isRtl = this . dir ( ) === 'rtl' ;
307+ const transformValue = isRtl ? - targetStyle . right : targetStyle . left ;
308+
279309 return {
280- transform : element ?. offsetTop ?? 0 ,
281- width : element ?. clientWidth ?? 0 ,
282- height : element ?. clientHeight ?? 0 ,
283- vertical
310+ transform : `translateX(${ transformValue } px)` ,
311+ width : `${ targetStyle . width } px` ,
312+ height : '100%'
284313 } ;
285314 }
286- return {
287- transform : element ?. offsetLeft ?? 0 ,
288- width : element ?. clientWidth ?? 0
289- } ;
315+
316+ protected handleTransitionEnd ( event : TransitionEvent ) : void {
317+ if ( event . propertyName === 'transform' ) {
318+ this . service . animating$ . next ( false ) ;
319+ }
320+ }
290321}
0 commit comments