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';
17
18const EMPTY_STRING: string = '';
19const MAX_PROGRESS: number = 100;
20const MAX_PERCENTAGE: string = '100%';
21const MIN_PERCENTAGE: string = '0%';
22const TEXT_OPACITY: number = 0.4;
23const BUTTON_NORMARL_WIDTH: number = 44;
24const BUTTON_NORMARL_HEIGHT: number = 28;
25const BUTTON_BORDER_RADIUS: number = 14;
26const TEXT_ENABLE: number = 1.0;
27
28
29// Set the key value for the basic component of skin changing corresponding to progressButton
30const PROGRESS_BUTTON_PROGRESS_KEY = 'progress_button_progress_key';
31const PROGRESS_BUTTON_PRIMARY_FONT_KEY = 'progress_button_primary_font_key';
32const PROGRESS_BUTTON_CONTAINER_BACKGROUND_COLOR_KEY = 'progress_button_container_background_color_key';
33const PROGRESS_BUTTON_EMPHASIZE_SECONDARY_BUTTON_KEY = 'progress_button_emphasize_secondary_button_key';
34@Component
35export struct ProgressButton {
36  @Prop @Watch('getProgressContext') progress: number;
37  @State textProgress: string = EMPTY_STRING;
38  @Prop content: string = EMPTY_STRING;
39  @State @Watch('getLoadingProgress')isLoading: boolean = false;
40  progressButtonWidth?: Length = BUTTON_NORMARL_WIDTH;
41  clickCallback: () => void = () => {};
42  @Prop enable: boolean = true;
43
44  @State progressColor: ResourceColor = '#330A59F7'
45  @State containerBorderColor: ResourceColor = '#330A59F7'
46  @State containerBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_foreground_contrary')
47  @State textHeight?: Length = BUTTON_NORMARL_HEIGHT;
48  @State buttonBorderRadius?: number = BUTTON_BORDER_RADIUS;
49
50  onWillApplyTheme(theme: Theme) {
51    this.progressColor = theme.colors.compEmphasizeSecondary;
52    this.containerBorderColor = theme.colors.compEmphasizeSecondary;
53    this.containerBackgroundColor = theme.colors.iconOnFourth;
54  }
55
56  private getButtonProgress(): number {
57    if (this.progress < 0) {
58      return 0
59    } else if (this.progress > MAX_PROGRESS) {
60      return MAX_PROGRESS
61    }
62    return this.progress
63  }
64
65  private getProgressContext() {
66    if (this.progress < 0) {
67      this.isLoading = false
68      this.textProgress = MIN_PERCENTAGE
69    } else if (this.progress >= MAX_PROGRESS) {
70      this.isLoading = false
71      this.textProgress = MAX_PERCENTAGE
72    } else {
73      this.isLoading = true
74      this.textProgress = Math.floor(this.progress / MAX_PROGRESS * MAX_PROGRESS).toString() + '%'
75    }
76  }
77
78  private getLoadingProgress() {
79    if (this.isLoading) {
80      if (this.progress < 0) {
81        this.textProgress = MIN_PERCENTAGE
82      } else if (this.progress >= MAX_PROGRESS) {
83        this.textProgress = MAX_PERCENTAGE
84      } else {
85        this.textProgress = Math.floor(this.progress / MAX_PROGRESS * MAX_PROGRESS).toString() + '%'
86      }
87    }
88  }
89
90  build() {
91    Button() {
92      Stack(){
93        Progress({ value: this.getButtonProgress(), total: MAX_PROGRESS,
94          style: ProgressStyle.Capsule })
95          .height(this.textHeight)
96          .constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
97          .borderRadius(this.buttonBorderRadius)
98          .width('100%')
99          .hoverEffect(HoverEffect.None)
100          .clip(false)
101          .enabled(this.enable)
102          .key(PROGRESS_BUTTON_PROGRESS_KEY)
103          .color(this.progressColor)
104        Row() {
105          Text(this.isLoading ? this.textProgress: this.content)
106            .fontSize($r('sys.float.ohos_id_text_size_button3'))
107            .fontWeight(FontWeight.Medium)
108            .key(PROGRESS_BUTTON_PRIMARY_FONT_KEY)
109            .maxLines(1)
110            .textOverflow({ overflow: TextOverflow.Ellipsis })
111            .padding({ top: 4, left: 8, right: 8, bottom: 4})
112            .opacity(this.enable ? TEXT_ENABLE : TEXT_OPACITY)
113            .onAreaChange((_, newValue)=>{
114              if (!newValue.height || newValue.height === this.textHeight) {
115                return;
116              }
117              this.textHeight = newValue.height > BUTTON_NORMARL_HEIGHT ? newValue.height : BUTTON_NORMARL_HEIGHT;
118              this.buttonBorderRadius = Number(this.textHeight) / 2;
119            })
120        }
121        .constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
122        Row()
123          .key(PROGRESS_BUTTON_CONTAINER_BACKGROUND_COLOR_KEY)
124          .backgroundColor(Color.Transparent)
125          .border({ width: 1, color: this.containerBorderColor})
126          .height(this.textHeight)
127          .constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
128          .borderRadius(this.buttonBorderRadius)
129          .width('100%')
130      }
131    }
132    .borderRadius(this.buttonBorderRadius)
133    .clip(false)
134    .hoverEffect(HoverEffect.None)
135    .key(PROGRESS_BUTTON_EMPHASIZE_SECONDARY_BUTTON_KEY)
136    .backgroundColor(this.containerBackgroundColor)
137    .constraintSize({minWidth: 44})
138    .padding({ top: 0, bottom: 0})
139    .width((!this.progressButtonWidth || this.progressButtonWidth < BUTTON_NORMARL_WIDTH) ?
140      BUTTON_NORMARL_WIDTH : this.progressButtonWidth)
141    .stateEffect(this.enable)
142    .onClick(() => {
143      if (!this.enable) {
144        return
145      }
146      if (this.progress < MAX_PROGRESS) {
147        this.isLoading = !this.isLoading
148      }
149      this.clickCallback && this.clickCallback()
150    })
151  }
152}