/*
* Copyright (c) 2024 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 '@ohos.arkui.node';
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';
@ComponentV2
export struct ProgressButtonV2 {
@Param @Require progress: number;
@Param content: ResourceStr = EMPTY_STRING;
@Param @Once progressButtonWidth?: LengthMetrics = LengthMetrics.vp(BUTTON_NORMARL_WIDTH);
@Param onClicked: ClickCallback = () => {
};
@Param isEnabled: boolean = true;
@Param colorOptions?: ProgressButtonV2Color | undefined = undefined;
@Param progressButtonRadius?: LengthMetrics | undefined = undefined;
@Local textProgress: string = EMPTY_STRING;
@Local isLoading: boolean = false;
@Local progressColor: ResourceColor = '#330A59F7'
@Local containerBorderColor: ResourceColor = '#330A59F7'
@Local containerBackgroundColor: ResourceColor = $r('sys.color.ohos_id_color_foreground_contrary')
@Local textHeight?: Length = BUTTON_NORMARL_HEIGHT;
@Local 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;
}
@Monitor('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)}%`;
}
}
@Monitor('isLoading')
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)}%`;
}
}
}
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;
}
}
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({
borderColor: this.colorOptions?.borderColor?.color ?
this.colorOptions?.borderColor?.color : this.containerBorderColor,
borderRadius: this.getProgressButtonRadius()
})
.clip(false)
.enabled(this.isEnabled)
.key(PROGRESS_BUTTON_PROGRESS_KEY)
.color(this.colorOptions?.progressColor?.color ? this.colorOptions?.progressColor?.color : 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?.color)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.padding({
top: 4,
left: 8,
right: 8,
bottom: 4
})
.opacity(this.isEnabled ? TEXT_ENABLE : TEXT_OPACITY)
.onSizeChange((_, 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?.color ?
this.colorOptions?.borderColor?.color : 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?.color
? this.colorOptions?.backgroundColor?.color
: this.containerBackgroundColor)
.padding({ top: 0, bottom: 0 })
.width((!this.progressButtonWidth || this.progressButtonWidth.value < 0) ?
BUTTON_NORMARL_WIDTH : this.toLengthString(this.progressButtonWidth))
.constraintSize({ minWidth: BUTTON_NORMARL_WIDTH })
.stateEffect(this.isEnabled)
.onClick(() => {
if (!this.isEnabled) {
return;
}
if (this.progress < MAX_PROGRESS) {
this.isLoading = !this.isLoading;
}
this.onClicked?.();
})
}
private toLengthString(value: LengthMetrics | undefined): string {
if (value === void (0)) {
return '';
}
const length: number = value.value;
switch (value.unit) {
case LengthUnit.PX:
return `${length}px`;
case LengthUnit.FP:
return `${length}fp`;
case LengthUnit.LPX:
return `${length}lpx`;
case LengthUnit.PERCENT:
return `${length * 100}%`;
case LengthUnit.VP:
return `${length}vp`;
default:
return `${length}vp`;
}
}
}
@ObservedV2
export class ProgressButtonV2Color {
@Trace public progressColor?: ColorMetrics;
@Trace public borderColor?: ColorMetrics;
@Trace public textColor?: ColorMetrics;
@Trace public backgroundColor?: ColorMetrics;
constructor(options: ProgressButtonV2ColorOptions) {
this.progressColor = options.progressColor;
this.borderColor = options.borderColor;
this.textColor = options.textColor;
this.backgroundColor = options.backgroundColor;
}
}
export type ClickCallback = () => void;
export interface ProgressButtonV2ColorOptions {
progressColor?: ColorMetrics;
borderColor?: ColorMetrics;
textColor?: ColorMetrics;
backgroundColor?: ColorMetrics;
}