@@ -5,26 +5,42 @@ import {
55 queryAssignedElements ,
66 state ,
77} from 'lit/decorators.js' ;
8-
8+ import type { StyleInfo } from 'lit/directives/style-map.js' ;
99import { watch } from '../common/decorators/watch.js' ;
10- import { asPercent , clamp , formatString } from '../common/util.js' ;
10+ import {
11+ asPercent ,
12+ clamp ,
13+ formatString ,
14+ isEmpty ,
15+ partNameMap ,
16+ } from '../common/util.js' ;
1117import type { StyleVariant } from '../types.js' ;
1218
1319export abstract class IgcProgressBaseComponent extends LitElement {
14- private __internals : ElementInternals ;
15- private _ticker ! : number ;
20+ private readonly __internals : ElementInternals ;
1621
1722 @queryAssignedElements ( )
18- protected assignedElements ! : Array < HTMLElement > ;
23+ protected _assignedElements ! : HTMLElement [ ] ;
24+
25+ @query ( '[part="base"]' , true )
26+ protected _base ! : HTMLElement ;
1927
20- @query ( '[part~="fill"]' , true )
21- protected progressIndicator ! : Element ;
28+ @state ( )
29+ protected _percentage = 0 ;
30+
31+ @state ( )
32+ protected _progress = 0 ;
2233
2334 @state ( )
24- protected percentage = 0 ;
35+ protected _hasFraction = false ;
2536
2637 @state ( )
27- protected progress = 0 ;
38+ protected _styleInfo : StyleInfo = {
39+ '--_progress-whole' : '0.00' ,
40+ '--_progress-integer' : '0' ,
41+ '--_progress-fraction' : '0' ,
42+ '--_transition-duration' : '0ms' ,
43+ } ;
2844
2945 /**
3046 * Maximum value of the control.
@@ -78,49 +94,43 @@ export abstract class IgcProgressBaseComponent extends LitElement {
7894 @property ( { attribute : 'label-format' } )
7995 public labelFormat ! : string ;
8096
81- @watch ( 'indeterminate' , { waitUntilFirstUpdate : true } )
97+ @watch ( 'indeterminate' )
8298 protected indeterminateChange ( ) {
83- this . cancelAnimations ( ) ;
84-
8599 if ( ! this . indeterminate ) {
86- this . _setProgress ( ) ;
87- this . animateLabelTo ( 0 , this . value ) ;
100+ this . _updateProgress ( ) ;
88101 }
89102 }
90103
91- @watch ( 'max' , { waitUntilFirstUpdate : true } )
104+ @watch ( 'max' )
92105 protected maxChange ( ) {
93106 this . max = Math . max ( 0 , this . max ) ;
94-
95107 if ( this . value > this . max ) {
96108 this . value = this . max ;
97109 }
98110
99- this . _setProgress ( ) ;
100-
101111 if ( ! this . indeterminate ) {
102- cancelAnimationFrame ( this . _ticker ) ;
103- this . animateLabelTo ( this . max , this . value ) ;
112+ this . _updateProgress ( ) ;
104113 }
105114 }
106115
107- @watch ( 'value' , { waitUntilFirstUpdate : true } )
108- protected valueChange ( previous : number ) {
116+ @watch ( 'value' )
117+ protected valueChange ( ) {
109118 this . value = clamp ( this . value , 0 , this . max ) ;
110- this . _setProgress ( ) ;
111119
112120 if ( ! this . indeterminate ) {
113- cancelAnimationFrame ( this . _ticker ) ;
114- this . animateLabelTo ( previous , this . value ) ;
121+ this . _updateProgress ( ) ;
115122 }
116123 }
117124
118125 constructor ( ) {
119126 super ( ) ;
120127 this . __internals = this . attachInternals ( ) ;
121128
122- this . __internals . role = 'progressbar' ;
123- this . __internals . ariaValueMin = '0' ;
129+ Object . assign ( this . __internals , {
130+ role : 'progressbar' ,
131+ ariaValueMin : '0' ,
132+ ariaValueNow : '0' ,
133+ } ) ;
124134 }
125135
126136 protected override createRenderRoot ( ) {
@@ -134,83 +144,51 @@ export abstract class IgcProgressBaseComponent extends LitElement {
134144 }
135145
136146 private _updateARIA ( ) {
137- const internals = this . __internals ;
138- const text = this . labelFormat
139- ? this . renderLabelFormat ( )
140- : `${ this . percentage } %` ;
141-
142- internals . ariaValueMax = `${ this . max } ` ;
143- internals . ariaValueNow = this . indeterminate ? null : `${ this . value } ` ;
144- internals . ariaValueText = this . indeterminate ? null : text ;
145- }
147+ const text = this . labelFormat ? this . renderLabelFormat ( ) : `${ this . value } %` ;
146148
147- private _setProgress ( ) {
148- this . progress = this . value / this . max ;
149+ Object . assign ( this . __internals , {
150+ ariaValueMax : this . max . toString ( ) ,
151+ ariaValueNow : this . indeterminate ? null : this . value . toString ( ) ,
152+ ariaValueText : this . indeterminate ? null : text ,
153+ } ) ;
149154 }
150155
151- public override async connectedCallback ( ) {
152- super . connectedCallback ( ) ;
156+ private _updateProgress ( ) {
157+ const percentage = asPercent ( this . value , Math . max ( 1 , this . max ) ) ;
158+ const fractionValue = Math . round ( ( percentage % 1 ) * 100 ) ;
159+ this . _hasFraction = fractionValue > 0 ;
153160
154- await this . updateComplete ;
155- if ( ! this . indeterminate ) {
156- requestAnimationFrame ( ( ) => {
157- this . _setProgress ( ) ;
158- this . animateLabelTo ( 0 , this . value ) ;
159- } ) ;
160- }
161+ this . _styleInfo = {
162+ '--_progress-whole' : percentage . toFixed ( 2 ) ,
163+ '--_progress-integer' : Math . floor ( percentage ) ,
164+ '--_progress-fraction' : fractionValue ,
165+ '--_transition-duration' : `${ this . animationDuration } ms` ,
166+ } ;
161167 }
162168
163- protected cancelAnimations ( ) {
164- cancelAnimationFrame ( this . _ticker ) ;
165- this . progressIndicator ?. getAnimations ( ) . forEach ( ( animation ) => {
166- if ( animation instanceof CSSTransition ) {
167- animation . cancel ( ) ;
168- }
169+ protected renderLabel ( ) {
170+ const parts = partNameMap ( {
171+ label : true ,
172+ value : true ,
173+ fraction : this . _hasFraction ,
169174 } ) ;
170- }
171-
172- protected animateLabelTo ( start : number , end : number ) {
173- let t0 : number ;
174-
175- const tick = ( t1 : number ) => {
176- t0 = t0 ?? t1 ;
177175
178- const delta = Math . min (
179- ( t1 - t0 ) / Math . max ( this . animationDuration , 1 ) ,
180- 1
181- ) ;
182-
183- this . percentage = Math . floor (
184- asPercent ( delta * ( end - start ) + start , this . max )
185- ) ;
186-
187- if ( delta < 1 ) {
188- this . _ticker = requestAnimationFrame ( tick ) ;
189- } else {
190- cancelAnimationFrame ( this . _ticker ) ;
191- }
192- } ;
193-
194- requestAnimationFrame ( tick ) ;
176+ return this . labelFormat
177+ ? html `< span part =${ parts } > ${ this . renderLabelFormat ( ) } </ span > `
178+ : html `< span part ="${ parts } counter "> </ span > ` ;
195179 }
196180
197181 protected renderLabelFormat ( ) {
198182 return formatString ( this . labelFormat , this . value , this . max ) ;
199183 }
200184
201185 protected renderDefaultSlot ( ) {
202- const hasNoLabel =
203- this . indeterminate || this . hideLabel || this . assignedElements . length ;
186+ const hideDefaultLabel =
187+ this . indeterminate || this . hideLabel || ! isEmpty ( this . _assignedElements ) ;
204188
205189 return html `
206190 < slot part ="label "> </ slot >
207- ${ hasNoLabel
208- ? nothing
209- : html `< span part ="label value "> ${ this . renderLabelText ( ) } </ span > ` }
191+ ${ hideDefaultLabel ? nothing : this . renderLabel ( ) }
210192 ` ;
211193 }
212-
213- protected renderLabelText ( ) {
214- return this . labelFormat ? this . renderLabelFormat ( ) : `${ this . percentage } %` ;
215- }
216194}
0 commit comments