• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024 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 '@ohos.arkui.node';
18
19const EMPTY_STRING: string = '';
20const MAX_PROGRESS: number = 100;
21const MAX_PERCENTAGE: string = '100%';
22const MIN_PERCENTAGE: string = '0%';
23const TEXT_OPACITY: number = 0.4;
24const BUTTON_NORMARL_WIDTH: number = 44;
25const BUTTON_NORMARL_HEIGHT: number = 28;
26const BUTTON_BORDER_RADIUS: number = 14;
27const TEXT_ENABLE: number = 1.0;
28
29
30// Set the key value for the basic component of skin changing corresponding to progressButton
31const PROGRESS_BUTTON_PROGRESS_KEY = 'progress_button_progress_key';
32const PROGRESS_BUTTON_PRIMARY_FONT_KEY = 'progress_button_primary_font_key';
33const PROGRESS_BUTTON_CONTAINER_BACKGROUND_COLOR_KEY = 'progress_button_container_background_color_key';
34const PROGRESS_BUTTON_EMPHASIZE_SECONDARY_BUTTON_KEY = 'progress_button_emphasize_secondary_button_key';
35
36@ComponentV2
37export struct ProgressButtonV2 {
38  @Param @Require progress: number;
39  @Param content: ResourceStr = EMPTY_STRING;
40  @Param @Once progressButtonWidth?: LengthMetrics = LengthMetrics.vp(BUTTON_NORMARL_WIDTH);
41  @Param onClicked: ClickCallback = () => {
42  };
43  @Param isEnabled: boolean = true;
44  @Param colorOptions?: ProgressButtonV2Color | undefined = undefined;
45  @Param progressButtonRadius?: LengthMetrics | undefined = undefined;
46  @Local textProgress: string = EMPTY_STRING;
47  @Local isLoading: boolean = false;
48  @Local progressColor: ResourceColor = '#330A59F7'
49  @Local containerBorderColor: ResourceColor = '#330A59F7'
50  @Local containerBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_foreground_contrary')
51  @Local textHeight?: Length = BUTTON_NORMARL_HEIGHT;
52  @Local buttonBorderRadius?: number = BUTTON_BORDER_RADIUS;
53
54  onWillApplyTheme(theme: Theme) {
55    this.progressColor = theme.colors.compEmphasizeSecondary;
56    this.containerBorderColor = theme.colors.compEmphasizeSecondary;
57    this.containerBackgroundColor = theme.colors.iconOnFourth;
58  }
59
60  private getButtonProgress(): number {
61    if (this.progress < 0) {
62      return 0;
63    } else if (this.progress > MAX_PROGRESS) {
64      return MAX_PROGRESS;
65    }
66    return this.progress;
67  }
68
69  @Monitor('progress')
70  private getProgressContext() {
71    if (this.progress < 0) {
72      this.isLoading = false;
73      this.textProgress = MIN_PERCENTAGE;
74    } else if (this.progress >= MAX_PROGRESS) {
75      this.isLoading = false;
76      this.textProgress = MAX_PERCENTAGE;
77    } else {
78      this.isLoading = true;
79      this.textProgress = `${Math.floor(this.progress / MAX_PROGRESS * MAX_PROGRESS)}%`;
80    }
81  }
82
83  @Monitor('isLoading')
84  private getLoadingProgress() {
85    if (this.isLoading) {
86      if (this.progress < 0) {
87        this.textProgress = MIN_PERCENTAGE;
88      } else if (this.progress >= MAX_PROGRESS) {
89        this.textProgress = MAX_PERCENTAGE;
90      } else {
91        this.textProgress = `${Math.floor(this.progress / MAX_PROGRESS * MAX_PROGRESS)}%`;
92      }
93    }
94  }
95
96  private getProgressButtonRadius(): LengthMetrics {
97    if (!this.progressButtonRadius || this.progressButtonRadius.unit === LengthUnit.PERCENT) {
98      return LengthMetrics.vp(this.buttonBorderRadius);
99    } else if (this.progressButtonRadius.value < 0) {
100      return LengthMetrics.vp(0);
101    } else {
102      return this.progressButtonRadius;
103    }
104  }
105
106  build() {
107    Button() {
108      Stack() {
109        Progress({
110          value: this.getButtonProgress(), total: MAX_PROGRESS,
111          style: ProgressStyle.Capsule
112        })
113          .height(this.textHeight)
114          .constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
115          .borderRadius(this.buttonBorderRadius)
116          .width('100%')
117          .hoverEffect(HoverEffect.None)
118          .style({
119            borderColor: this.colorOptions?.borderColor?.color ?
120              this.colorOptions?.borderColor?.color : this.containerBorderColor,
121            borderRadius: this.getProgressButtonRadius()
122          })
123          .clip(false)
124          .enabled(this.isEnabled)
125          .key(PROGRESS_BUTTON_PROGRESS_KEY)
126          .color(this.colorOptions?.progressColor?.color ? this.colorOptions?.progressColor?.color : this.progressColor)
127        Row() {
128          Text(this.isLoading ? this.textProgress : this.content)
129            .fontSize($r('sys.float.ohos_id_text_size_button3'))
130            .fontWeight(FontWeight.Medium)
131            .key(PROGRESS_BUTTON_PRIMARY_FONT_KEY)
132            .fontColor(this.colorOptions?.textColor?.color)
133            .maxLines(1)
134            .textOverflow({ overflow: TextOverflow.Ellipsis })
135            .padding({
136              top: 4,
137              left: 8,
138              right: 8,
139              bottom: 4
140            })
141            .opacity(this.isEnabled ? TEXT_ENABLE : TEXT_OPACITY)
142            .onSizeChange((_, newValue) => {
143              if (!newValue.height || newValue.height === this.textHeight) {
144                return;
145              }
146              this.textHeight = newValue.height > BUTTON_NORMARL_HEIGHT ? newValue.height : BUTTON_NORMARL_HEIGHT;
147              this.buttonBorderRadius = Number(this.textHeight) / 2;
148            })
149        }
150        .constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
151
152        Row()
153          .key(PROGRESS_BUTTON_CONTAINER_BACKGROUND_COLOR_KEY)
154          .backgroundColor(Color.Transparent)
155          .border({
156            width: 1,
157            color: this.colorOptions?.borderColor?.color ?
158              this.colorOptions?.borderColor?.color : this.containerBorderColor
159          })
160          .height(this.textHeight)
161          .constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
162          .borderRadius(this.progressButtonRadius ? this.toLengthString(this.getProgressButtonRadius()) :
163          this.buttonBorderRadius)
164          .width('100%')
165      }
166    }
167    .borderRadius(this.progressButtonRadius ? this.toLengthString(this.getProgressButtonRadius()) :
168    this.buttonBorderRadius)
169    .clip(false)
170    .hoverEffect(HoverEffect.None)
171    .key(PROGRESS_BUTTON_EMPHASIZE_SECONDARY_BUTTON_KEY)
172    .backgroundColor(
173      this.colorOptions?.backgroundColor?.color
174        ? this.colorOptions?.backgroundColor?.color
175        : this.containerBackgroundColor)
176    .padding({ top: 0, bottom: 0 })
177    .width((!this.progressButtonWidth || this.progressButtonWidth.value < 0) ?
178      BUTTON_NORMARL_WIDTH : this.toLengthString(this.progressButtonWidth))
179    .constraintSize({ minWidth: BUTTON_NORMARL_WIDTH })
180    .stateEffect(this.isEnabled)
181    .onClick(() => {
182      if (!this.isEnabled) {
183        return;
184      }
185      if (this.progress < MAX_PROGRESS) {
186        this.isLoading = !this.isLoading;
187      }
188      this.onClicked?.();
189    })
190  }
191
192  private toLengthString(value: LengthMetrics | undefined): string {
193    if (value === void (0)) {
194      return '';
195    }
196    const length: number = value.value;
197    switch (value.unit) {
198      case LengthUnit.PX:
199        return `${length}px`;
200      case LengthUnit.FP:
201        return `${length}fp`;
202      case LengthUnit.LPX:
203        return `${length}lpx`;
204      case LengthUnit.PERCENT:
205        return `${length * 100}%`;
206      case LengthUnit.VP:
207        return `${length}vp`;
208      default:
209        return `${length}vp`;
210    }
211  }
212}
213
214@ObservedV2
215export class ProgressButtonV2Color {
216  @Trace public progressColor?: ColorMetrics;
217  @Trace public borderColor?: ColorMetrics;
218  @Trace public textColor?: ColorMetrics;
219  @Trace public backgroundColor?: ColorMetrics;
220
221  constructor(options: ProgressButtonV2ColorOptions) {
222    this.progressColor = options.progressColor;
223    this.borderColor = options.borderColor;
224    this.textColor = options.textColor;
225    this.backgroundColor = options.backgroundColor;
226  }
227}
228
229export type ClickCallback = () => void;
230
231export interface ProgressButtonV2ColorOptions {
232  progressColor?: ColorMetrics;
233  borderColor?: ColorMetrics;
234  textColor?: ColorMetrics;
235  backgroundColor?: ColorMetrics;
236}