/*
* Copyright (c) 2023-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Theme } from '@ohos.arkui.theme';
import { LengthMetrics, LengthUnit } from '@kit.ArkUI';
export interface ProgressButtonColorOptions {
progressColor?: ResourceColor;
borderColor?: ResourceColor;
textColor?: ResourceColor;
backgroundColor?: ResourceColor;
}
const EMPTY_STRING: string = '';
const MAX_PROGRESS: number = 100;
const MAX_PERCENTAGE: string = '100%';
const MIN_PERCENTAGE: string = '0%';
const TEXT_OPACITY: number = 0.4;
const BUTTON_NORMARL_WIDTH: number = 44;
const BUTTON_NORMARL_HEIGHT: number = 28;
const BUTTON_BORDER_RADIUS: number = 14;
const TEXT_ENABLE: number = 1.0;
// Set the key value for the basic component of skin changing corresponding to progressButton
const PROGRESS_BUTTON_PROGRESS_KEY = 'progress_button_progress_key';
const PROGRESS_BUTTON_PRIMARY_FONT_KEY = 'progress_button_primary_font_key';
const PROGRESS_BUTTON_CONTAINER_BACKGROUND_COLOR_KEY = 'progress_button_container_background_color_key';
const PROGRESS_BUTTON_EMPHASIZE_SECONDARY_BUTTON_KEY = 'progress_button_emphasize_secondary_button_key';
@Component
export struct ProgressButton {
@Prop @Watch('getProgressContext') progress: number;
@State textProgress: string = EMPTY_STRING;
@Prop content: ResourceStr = EMPTY_STRING;
@State @Watch('getLoadingProgress')isLoading: boolean = false;
progressButtonWidth?: Length = BUTTON_NORMARL_WIDTH;
clickCallback: () => void = () => {};
@Prop enable: boolean = true;
@Prop colorOptions: ProgressButtonColorOptions | undefined = undefined;
@Prop progressButtonRadius?: LengthMetrics | undefined = undefined;
@State progressColor: ResourceColor = '#330A59F7'
@State containerBorderColor: ResourceColor = '#330A59F7'
@State containerBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_foreground_contrary')
@State textHeight?: Length = BUTTON_NORMARL_HEIGHT;
@State buttonBorderRadius?: number = BUTTON_BORDER_RADIUS;
onWillApplyTheme(theme: Theme) {
this.progressColor = theme.colors.compEmphasizeSecondary;
this.containerBorderColor = theme.colors.compEmphasizeSecondary;
this.containerBackgroundColor = theme.colors.iconOnFourth;
}
private getButtonProgress(): number {
if (this.progress < 0) {
return 0
} else if (this.progress > MAX_PROGRESS) {
return MAX_PROGRESS
}
return this.progress
}
private getProgressContext() {
if (this.progress < 0) {
this.isLoading = false
this.textProgress = MIN_PERCENTAGE
} else if (this.progress >= MAX_PROGRESS) {
this.isLoading = false
this.textProgress = MAX_PERCENTAGE
} else {
this.isLoading = true
this.textProgress = Math.floor(this.progress / MAX_PROGRESS * MAX_PROGRESS).toString() + '%'
}
}
private getProgressButtonRadius(): LengthMetrics {
if (!this.progressButtonRadius || this.progressButtonRadius.unit === LengthUnit.PERCENT) {
return LengthMetrics.vp(this.buttonBorderRadius);
} else if (this.progressButtonRadius.value < 0) {
return LengthMetrics.vp(0);
} else {
return this.progressButtonRadius;
}
}
private getLoadingProgress() {
if (this.isLoading) {
if (this.progress < 0) {
this.textProgress = MIN_PERCENTAGE
} else if (this.progress >= MAX_PROGRESS) {
this.textProgress = MAX_PERCENTAGE
} else {
this.textProgress = Math.floor(this.progress / MAX_PROGRESS * MAX_PROGRESS).toString() + '%'
}
}
}
private toLengthString(value: LengthMetrics | undefined): string {
if (value === void (0)) {
return '';
}
const length: number = value.value;
let lengthString: string = '';
switch (value.unit) {
case LengthUnit.PX:
lengthString = `${length}px`;
break;
case LengthUnit.FP:
lengthString = `${length}fp`;
break;
case LengthUnit.LPX:
lengthString = `${length}lpx`;
break;
case LengthUnit.PERCENT:
lengthString = `${length * 100}%`;
break;
case LengthUnit.VP:
lengthString = `${length}vp`;
break;
default:
lengthString = `${length}vp`;
break;
}
return lengthString;
}
build() {
Button() {
Stack(){
Progress({ value: this.getButtonProgress(), total: MAX_PROGRESS,
style: ProgressStyle.Capsule })
.height(this.textHeight)
.constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
.borderRadius(this.buttonBorderRadius)
.width('100%')
.hoverEffect(HoverEffect.None)
.style(this.progressButtonRadius ?
{ borderRadius: this.getProgressButtonRadius() } : {})
.clip(false)
.key(PROGRESS_BUTTON_PROGRESS_KEY)
.color(this.colorOptions?.progressColor ? this.colorOptions?.progressColor : this.progressColor)
Row() {
Text(this.isLoading ? this.textProgress: this.content)
.fontSize($r('sys.float.ohos_id_text_size_button3'))
.fontWeight(FontWeight.Medium)
.key(PROGRESS_BUTTON_PRIMARY_FONT_KEY)
.fontColor(this.colorOptions?.textColor)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.padding({ top: 4, left: 8, right: 8, bottom: 4})
.opacity(this.enable ? TEXT_ENABLE : TEXT_OPACITY)
.onAreaChange((_, newValue)=>{
if (!newValue.height || newValue.height === this.textHeight) {
return;
}
this.textHeight = newValue.height > BUTTON_NORMARL_HEIGHT ? newValue.height : BUTTON_NORMARL_HEIGHT;
this.buttonBorderRadius = Number(this.textHeight) / 2;
})
}
.constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
Row()
.key(PROGRESS_BUTTON_CONTAINER_BACKGROUND_COLOR_KEY)
.backgroundColor(Color.Transparent)
.border({
width: 1,
color: this.colorOptions?.borderColor ? this.colorOptions?.borderColor : this.containerBorderColor
})
.height(this.textHeight)
.constraintSize({ minHeight: BUTTON_NORMARL_HEIGHT })
.borderRadius(this.progressButtonRadius ? this.toLengthString(this.getProgressButtonRadius()) :
this.buttonBorderRadius)
.width('100%')
}
}
.borderRadius(this.progressButtonRadius ? this.toLengthString(this.getProgressButtonRadius()) :
this.buttonBorderRadius)
.clip(false)
.hoverEffect(HoverEffect.None)
.key(PROGRESS_BUTTON_EMPHASIZE_SECONDARY_BUTTON_KEY)
.backgroundColor(
this.colorOptions?.backgroundColor
? this.colorOptions?.backgroundColor
: this.containerBackgroundColor)
.constraintSize({minWidth: 44})
.padding({ top: 0, bottom: 0})
.width((!this.progressButtonWidth || this.progressButtonWidth < BUTTON_NORMARL_WIDTH) ?
BUTTON_NORMARL_WIDTH : this.progressButtonWidth)
.stateEffect(this.enable)
.onClick(() => {
if (!this.enable) {
return
}
if (this.progress < MAX_PROGRESS) {
this.isLoading = !this.isLoading
}
this.clickCallback && this.clickCallback()
})
}
}