• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { Theme } from '@ohos.arkui.theme';
17import { LengthMetrics, LengthUnit } from '@kit.ArkUI';
18
19export interface ProgressButtonColorOptions {
20  progressColor?: ResourceColor;
21  borderColor?: ResourceColor;
22  textColor?: ResourceColor;
23  backgroundColor?: ResourceColor;
24}
25
26const EMPTY_STRING: string = '';
27const MAX_PROGRESS: number = 100;
28const MAX_PERCENTAGE: string = '100%';
29const MIN_PERCENTAGE: string = '0%';
30const TEXT_OPACITY: number = 0.4;
31const BUTTON_NORMARL_WIDTH: number = 44;
32const BUTTON_NORMARL_HEIGHT: number = 28;
33const BUTTON_BORDER_RADIUS: number = 14;
34const TEXT_ENABLE: number = 1.0;
35
36
37// Set the key value for the basic component of skin changing corresponding to progressButton
38const PROGRESS_BUTTON_PROGRESS_KEY = 'progress_button_progress_key';
39const PROGRESS_BUTTON_PRIMARY_FONT_KEY = 'progress_button_primary_font_key';
40const PROGRESS_BUTTON_CONTAINER_BACKGROUND_COLOR_KEY = 'progress_button_container_background_color_key';
41const PROGRESS_BUTTON_EMPHASIZE_SECONDARY_BUTTON_KEY = 'progress_button_emphasize_secondary_button_key';
42@Component
43export struct ProgressButton {
44  @Prop @Watch('getProgressContext') progress: number;
45  @State textProgress: string = EMPTY_STRING;
46  @Prop content: string = EMPTY_STRING;
47  @State @Watch('getLoadingProgress')isLoading: boolean = false;
48  progressButtonWidth?: Length = BUTTON_NORMARL_WIDTH;
49  clickCallback: () => void = () => {};
50  @Prop enable: boolean = true;
51  @Prop colorOptions: ProgressButtonColorOptions | undefined = undefined;
52  @Prop progressButtonRadius?: LengthMetrics | undefined = undefined;
53
54  @State progressColor: ResourceColor = '#330A59F7'
55  @State containerBorderColor: ResourceColor = '#330A59F7'
56  @State containerBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_foreground_contrary')
57  @State textHeight?: Length = BUTTON_NORMARL_HEIGHT;
58  @State buttonBorderRadius?: number = BUTTON_BORDER_RADIUS;
59
60  onWillApplyTheme(theme: Theme) {
61    this.progressColor = theme.colors.compEmphasizeSecondary;
62    this.containerBorderColor = theme.colors.compEmphasizeSecondary;
63    this.containerBackgroundColor = theme.colors.iconOnFourth;
64  }
65
66  private getButtonProgress(): number {
67    if (this.progress < 0) {
68      return 0
69    } else if (this.progress > MAX_PROGRESS) {
70      return MAX_PROGRESS
71    }
72    return this.progress
73  }
74
75  private getProgressContext() {
76    if (this.progress < 0) {
77      this.isLoading = false
78      this.textProgress = MIN_PERCENTAGE
79    } else if (this.progress >= MAX_PROGRESS) {
80      this.isLoading = false
81      this.textProgress = MAX_PERCENTAGE
82    } else {
83      this.isLoading = true
84      this.textProgress = Math.floor(this.progress / MAX_PROGRESS * MAX_PROGRESS).toString() + '%'
85    }
86  }
87
88  private getProgressButtonRadius(): LengthMetrics {
89    if (!this.progressButtonRadius || this.progressButtonRadius.unit === LengthUnit.PERCENT) {
90      return LengthMetrics.vp(this.buttonBorderRadius);
91    } else if (this.progressButtonRadius.value < 0) {
92      return LengthMetrics.vp(0);
93    } else {
94      return this.progressButtonRadius;
95    }
96  }
97
98  private getLoadingProgress() {
99    if (this.isLoading) {
100      if(this.progress < 0) {
101        this.textProgress = MIN_PERCENTAGE
102      } else if (this.progress >= MAX_PROGRESS) {
103        this.textProgress = MAX_PERCENTAGE
104      } else {
105        this.textProgress = Math.floor(this.progress / MAX_PROGRESS * MAX_PROGRESS).toString() + '%'
106      }
107    }
108  }
109
110  private toLengthString(value: LengthMetrics | undefined): string {
111    if (value === void (0)) {
112      return '';
113    }
114    const length: number = value.value;
115    let lengthString: string = '';
116    switch (value.unit) {
117      case LengthUnit.PX:
118        lengthString = `${length}px`;
119        break;
120      case LengthUnit.FP:
121        lengthString = `${length}fp`;
122        break;
123      case LengthUnit.LPX:
124        lengthString = `${length}lpx`;
125        break;
126      case LengthUnit.PERCENT:
127        lengthString = `${length * 100}%`;
128        break;
129      case LengthUnit.VP:
130        lengthString = `${length}vp`;
131        break;
132      default:
133        lengthString = `${length}vp`;
134        break;
135    }
136    return lengthString;
137  }
138
139  build() {
140    Button() {
141      Stack(){
142        Progress({ value: this.getButtonProgress(), total: MAX_PROGRESS,
143          style: ProgressStyle.Capsule })
144          .height(this.textHeight)
145          .constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
146          .borderRadius(this.buttonBorderRadius)
147          .width('100%')
148          .hoverEffect(HoverEffect.None)
149          .style({
150            borderColor: this.colorOptions?.borderColor ? this.colorOptions?.borderColor : this.containerBorderColor,
151            borderRadius: this.getProgressButtonRadius()
152          })
153          .clip(false)
154          .key(PROGRESS_BUTTON_PROGRESS_KEY)
155          .color(this.colorOptions?.progressColor ? this.colorOptions?.progressColor : this.progressColor)
156        Row() {
157          Text(this.isLoading ? this.textProgress: this.content)
158            .fontSize($r('sys.float.ohos_id_text_size_button3'))
159            .fontWeight(FontWeight.Medium)
160            .key(PROGRESS_BUTTON_PRIMARY_FONT_KEY)
161            .fontColor(this.colorOptions?.textColor)
162            .maxLines(1)
163            .textOverflow({ overflow: TextOverflow.Ellipsis })
164            .padding({ top: 4, left: 8, right: 8, bottom: 4})
165            .opacity(this.enable ? TEXT_ENABLE : TEXT_OPACITY)
166            .onAreaChange((_, newValue)=>{
167              if (!newValue.height || newValue.height === this.textHeight) {
168                return;
169              }
170              this.textHeight = newValue.height > BUTTON_NORMARL_HEIGHT ? newValue.height : BUTTON_NORMARL_HEIGHT;
171              this.buttonBorderRadius = Number(this.textHeight) / 2;
172            })
173        }
174        .constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
175        Row()
176          .key(PROGRESS_BUTTON_CONTAINER_BACKGROUND_COLOR_KEY)
177          .backgroundColor(Color.Transparent)
178          .border({
179            width: 1,
180            color: this.colorOptions?.borderColor ? this.colorOptions?.borderColor : this.containerBorderColor
181          })
182          .height(this.textHeight)
183          .constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
184          .borderRadius(this.progressButtonRadius ? this.toLengthString(this.getProgressButtonRadius()) :
185          this.buttonBorderRadius)
186          .width('100%')
187      }
188    }
189    .borderRadius(this.progressButtonRadius ? this.toLengthString(this.getProgressButtonRadius()) :
190    this.buttonBorderRadius)
191    .clip(false)
192    .hoverEffect(HoverEffect.None)
193    .key(PROGRESS_BUTTON_EMPHASIZE_SECONDARY_BUTTON_KEY)
194    .backgroundColor(
195      this.colorOptions?.backgroundColor
196        ? this.colorOptions?.backgroundColor
197        : this.containerBackgroundColor)
198    .constraintSize({minWidth: 44})
199    .padding({ top: 0, bottom: 0})
200    .width((!this.progressButtonWidth || this.progressButtonWidth < BUTTON_NORMARL_WIDTH) ?
201      BUTTON_NORMARL_WIDTH : this.progressButtonWidth)
202    .stateEffect(this.enable)
203    .onClick(() => {
204      if (!this.enable) {
205        return
206      }
207      if (this.progress < MAX_PROGRESS) {
208        this.isLoading = !this.isLoading
209      }
210      this.clickCallback && this.clickCallback()
211    })
212  }
213}