/*
* Copyright (c) 2026 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 { ColorMetrics, LengthMetrics, LengthUnit } from '@ohos.arkui.node';
import { ImageModifier, SymbolGlyphModifier, TextModifier } from '@ohos.arkui.modifier';
import { KeyCode } from '@kit.InputKit';
import mediaquery from '@ohos.mediaquery';
import EnvironmentCallback from '@ohos.app.ability.EnvironmentCallback';
import common from '@ohos.app.ability.common';
import deviceInfo from '@ohos.deviceInfo';
import uiMaterial from '@ohos.arkui.uiMaterial';
export enum ChipV2Size {
NORMAL = 'NORMAL',
SMALL = 'SMALL'
}
enum IconType {
PREFIX_ICON = 'PREFIXICON',
SUFFIX_ICON = 'SUFFIXICON',
PREFIX_SYMBOL = 'PREFIXSYMBOL',
SUFFIX_SYMBOL = 'SUFFIXSYMBOL',
}
enum BreakPointsType {
SM = 'SM',
MD = 'MD',
LG = 'LG'
}
export enum ChipV2AccessibilitySelectedType {
CLICKED = 0,
CHECKED = 1,
SELECTED = 2,
}
export interface ChipV2ImageIconConfig {
src: ResourceStr;
size?: SizeT<LengthMetrics>;
fillColor?: ColorMetrics;
activatedFillColor?: ColorMetrics;
modifier?: ImageModifier;
}
@ObservedV2
export abstract class ChipV2Icon {
constructor() {}
}
@ObservedV2
export class ChipV2ImageIcon extends ChipV2Icon {
@Trace public src: ResourceStr;
@Trace public size?: SizeT<LengthMetrics>;
@Trace public fillColor?: ColorMetrics;
@Trace public activatedFillColor?: ColorMetrics;
@Trace public modifier?: ImageModifier;
constructor(config: ChipV2ImageIconConfig) {
super();
this.src = config.src;
this.size = config.size;
this.fillColor = config.fillColor;
this.activatedFillColor = config.activatedFillColor;
this.modifier = config.modifier;
}
}
export interface ChipV2SuffixImageIconConfig extends ChipV2ImageIconConfig, ChipV2AccessibilityConfig {
accessibilityLevel?: string;
accessibilityText?: ResourceStr;
accessibilityDescription?: ResourceStr;
action?: VoidCallback;
}
@ObservedV2
export class ChipV2SuffixImageIcon extends ChipV2ImageIcon {
@Trace public accessibilityLevel?: string;
@Trace public accessibilityText?: ResourceStr;
@Trace public accessibilityDescription?: ResourceStr;
@Trace public action?: VoidCallback;
constructor(config: ChipV2SuffixImageIconConfig) {
super(config);
this.accessibilityLevel = config.accessibilityLevel;
this.accessibilityText = config.accessibilityText;
this.accessibilityDescription = config.accessibilityDescription;
this.action = config.action;
}
}
export interface ChipV2PrefixImageIconConfig extends ChipV2ImageIconConfig {}
@ObservedV2
export class ChipV2PrefixImageIcon extends ChipV2ImageIcon {
constructor(config: ChipV2PrefixImageIconConfig) {
super(config);
}
}
export interface ChipV2AccessibilityConfig {
accessibilityLevel?: string;
accessibilityText?: ResourceStr;
accessibilityDescription?: ResourceStr;
}
@ObservedV2
export class ChipV2Accessibility {
@Trace public accessibilityLevel?: string;
@Trace public accessibilityText?: ResourceStr;
@Trace public accessibilityDescription?: ResourceStr;
constructor(config: ChipV2AccessibilityConfig) {
this.accessibilityLevel = config.accessibilityLevel;
this.accessibilityText = config.accessibilityText;
this.accessibilityDescription = config.accessibilityDescription;
}
}
export interface ChipV2CloseConfig extends ChipV2AccessibilityConfig {
fontSize?: LengthMetrics;
}
@ObservedV2
export class ChipV2CloseIcon extends ChipV2Accessibility {
@Trace public fontSize?: LengthMetrics;
constructor(config: ChipV2CloseConfig) {
super(config);
this.fontSize = config.fontSize;
}
}
export interface ChipV2SymbolIconConfig {
normal?: SymbolGlyphModifier;
activated?: SymbolGlyphModifier;
}
@ObservedV2
export class ChipV2SymbolIcon extends ChipV2Icon {
@Trace public normal?: SymbolGlyphModifier;
@Trace public activated?: SymbolGlyphModifier;
constructor(config: ChipV2SymbolIconConfig) {
super();
this.normal = config.normal;
this.activated = config.activated;
}
}
export interface ChipV2PrefixSymbolIconConfig extends ChipV2SymbolIconConfig {}
@ObservedV2
export class ChipV2PrefixSymbolIcon extends ChipV2SymbolIcon {
constructor(config: ChipV2PrefixSymbolIconConfig) {
super(config);
}
}
export interface ChipV2SuffixSymbolIconConfig extends ChipV2SymbolIconConfig {
normalAccessibility?: ChipV2AccessibilityConfig;
activatedAccessibility?: ChipV2AccessibilityConfig;
action?: VoidCallback;
}
@ObservedV2
export class ChipV2SuffixSymbolIcon extends ChipV2SymbolIcon {
@Trace public normalAccessibility?: ChipV2Accessibility;
@Trace public activatedAccessibility?: ChipV2Accessibility;
@Trace public action?: VoidCallback;
constructor(config: ChipV2SuffixSymbolIconConfig) {
super(config);
this.normalAccessibility =
config.normalAccessibility ? new ChipV2Accessibility(config.normalAccessibility) : undefined;
this.activatedAccessibility =
config.activatedAccessibility ? new ChipV2Accessibility(config.activatedAccessibility) : undefined;
this.action = config.action;
}
}
export interface ChipV2LabelMarginConfig {
left?: LengthMetrics;
right?: LengthMetrics;
}
export interface ChipV2LocalizedLabelMarginConfig {
start?: LengthMetrics;
end?: LengthMetrics;
}
export interface ChipV2LabelConfig {
text: string;
fontSize?: LengthMetrics;
fontColor?: ColorMetrics;
activatedFontColor?: ColorMetrics;
fontFamily?: string;
labelMargin?: ChipV2LabelMarginConfig;
localizedLabelMargin?: ChipV2LocalizedLabelMarginConfig;
modifier?: TextModifier;
}
@ObservedV2
export class ChipV2Label {
@Trace public text: string;
@Trace public fontSize?: LengthMetrics;
@Trace public fontColor?: ColorMetrics;
@Trace public activatedFontColor?: ColorMetrics;
@Trace public fontFamily?: string;
@Trace public labelMargin?: ChipV2LabelMarginConfig;
@Trace public localizedLabelMargin?: ChipV2LocalizedLabelMarginConfig;
@Trace public modifier?: TextModifier;
constructor(config: ChipV2LabelConfig) {
this.text = config.text;
this.fontSize = config.fontSize;
this.fontColor = config.fontColor;
this.activatedFontColor = config.activatedFontColor;
this.fontFamily = config.fontFamily;
this.labelMargin = config.labelMargin;
this.localizedLabelMargin = config.localizedLabelMargin;
this.modifier = config.modifier;
}
}
interface IconTheme {
normalSize: SizeOptions;
smallSize: SizeOptions;
fillColor: ResourceColor;
activatedFillColor: ResourceColor;
focusFillColor: ResourceColor;
focusActivatedColor: ResourceColor;
}
interface PrefixIconTheme extends IconTheme {}
interface SuffixIconTheme extends IconTheme {
defaultDeleteIcon: ResourceStr;
focusable: boolean;
isShowMargin: Resource;
}
interface DefaultSymbolTheme {
normalFontColor: Array<ResourceColor>;
activatedFontColor: Array<ResourceColor>;
smallSymbolFontSize: Length;
normalSymbolFontSize: Length;
defaultEffect: number;
}
interface LabelTheme {
normalFontSize: Dimension;
smallFontSize: Dimension;
adaptiveItemFontSize: Dimension;
focusFontColor: ResourceColor;
focusActiveFontColor: ResourceColor;
fontColor: ResourceColor;
activatedFontColor: ResourceColor;
fontFamily: string;
normalMargin: Margin;
localizedNormalMargin: LocalizedMargin;
smallMargin: Margin;
localizedSmallMargin: LocalizedMargin;
defaultFontSize: Dimension;
fontWeight: Resource;
activatedFontWeight: Resource;
}
interface ChipNodeOpacity {
normal: number;
hover: number;
pressed: number;
}
interface ChipNodeConstraintWidth {
breakPointMinWidth: number,
breakPointSmMaxWidth: number,
breakPointMdMaxWidth: number,
breakPointLgMaxWidth: number,
}
interface ChipNodeTheme {
suitAgeScale: number;
minLabelWidth: Dimension;
normalHeight: Dimension;
smallHeight: Dimension;
activatedNormalHeight: Dimension;
activatedSmallHeight: Dimension;
enabled: boolean;
activated: boolean;
backgroundColor: ResourceColor;
activatedBackgroundColor: ResourceColor;
focusOutlineColor: ResourceColor;
borderColor: ResourceColor,
defaultBorderWidth: Dimension;
activatedBorderColor: ResourceColor;
focusBtnScaleX: Resource;
focusBtnScaleY: Resource;
focusBgColor: ResourceColor;
focusActivatedBgColor: ResourceColor;
normalShadowStyle: Resource;
smallShadowStyle: Resource;
focusOutlineMargin: number;
normalBorderRadius: Dimension;
smallBorderRadius: Dimension;
borderWidth: number;
activatedBorderWidth: Dimension;
localizedNormalPadding: LocalizedPadding;
localizedSmallPadding: LocalizedPadding;
localizedActivatedNormalPadding: LocalizedPadding;
localizedActivatedSmallPadding: LocalizedPadding;
hoverBlendColor: ResourceColor;
pressedBlendColor: ResourceColor;
opacity: ChipNodeOpacity;
breakPointConstraintWidth: ChipNodeConstraintWidth;
}
interface ChipTheme {
prefixIcon: PrefixIconTheme;
label: LabelTheme;
suffixIcon: SuffixIconTheme;
defaultSymbol: DefaultSymbolTheme;
chipNode: ChipNodeTheme;
}
const RESOURCE_TYPE_STRING = 10003;
const RESOURCE_TYPE_FLOAT = 10002;
const RESOURCE_TYPE_INTEGER = 10007;
const HOT_SPOT_MIN_HEIGHT: number = 32;
class LengthMetricsUtils {
private static instance?: LengthMetricsUtils;
private constructor() {
}
public static getInstance(): LengthMetricsUtils {
if (!LengthMetricsUtils.instance) {
LengthMetricsUtils.instance = new LengthMetricsUtils();
}
return LengthMetricsUtils.instance;
}
isNaturalNumber(metrics: LengthMetrics): boolean {
return metrics.value >= 0;
}
}
class LengthMetricsCache {
private static _cache: Map<string, LengthMetrics> = new Map();
public static get(key: string, defaultValue: LengthMetrics): LengthMetrics {
if (LengthMetricsCache._cache.has(key)) {
return LengthMetricsCache._cache.get(key)!;
}
try {
const res: Resource = {
id: -1,
type: 10002,
params: [key],
bundleName: '__harDefaultBundleName__',
moduleName: '__harDefaultModuleName__',
};
const metrics = LengthMetrics.resource(res);
LengthMetricsCache._cache.set(key, metrics);
return metrics;
} catch (error) {
return defaultValue;
}
}
}
export interface IChipV2OptionsConfig {
label: ChipV2Label;
prefixIcon?: ChipV2Icon;
suffixIcon?: ChipV2Icon;
allowClose?: boolean;
closeIcon?: ChipV2CloseIcon;
enabled?: boolean;
activated?: boolean;
backgroundColor?: ColorMetrics;
activatedBackgroundColor?: ColorMetrics;
borderRadius?: LengthMetrics;
size?: ChipV2Size | SizeT<LengthMetrics>;
direction?: Direction;
accessibilityDescription?: ResourceStr;
accessibilityLevel?: string;
accessibilitySelectedType?: ChipV2AccessibilitySelectedType;
maxFontScale?: number | Resource;
minFontScale?: number | Resource;
padding?: LocalizedPadding;
fontSize?: LengthMetrics;
backgroundSystemMaterial?: uiMaterial.Material;
activatedBackgroundSystemMaterial?: uiMaterial.Material;
onClose?: VoidCallback;
onClicked?: Callback<void>;
}
@ObservedV2
export class ChipV2Options {
@Trace public readonly label: ChipV2Label;
@Trace public readonly prefixIcon?: ChipV2Icon;
@Trace public readonly suffixIcon?: ChipV2Icon;
@Trace public readonly allowClose?: boolean;
@Trace public readonly closeIcon?: ChipV2CloseIcon;
@Trace public readonly enabled?: boolean;
@Trace public readonly activated?: boolean;
@Trace public readonly backgroundColor?: ColorMetrics;
@Trace public readonly activatedBackgroundColor?: ColorMetrics;
@Trace public readonly borderRadius?: LengthMetrics;
@Trace public readonly size?: ChipV2Size | SizeT<LengthMetrics>;
@Trace public readonly direction?: Direction;
@Trace public readonly accessibilityDescription?: ResourceStr;
@Trace public readonly accessibilityLevel?: string;
@Trace public readonly accessibilitySelectedType?: ChipV2AccessibilitySelectedType;
@Trace public readonly maxFontScale?: number | Resource;
@Trace public readonly minFontScale?: number | Resource;
@Trace public readonly padding?: LocalizedPadding;
@Trace public readonly fontSize?: LengthMetrics;
@Trace public readonly backgroundSystemMaterial?: uiMaterial.Material;
@Trace public readonly activatedBackgroundSystemMaterial?: uiMaterial.Material;
public onClose?: VoidCallback;
public onClicked?: Callback<void>;
constructor(config: IChipV2OptionsConfig) {
this.label = config.label;
this.prefixIcon = config.prefixIcon;
this.suffixIcon = config.suffixIcon;
this.allowClose = config.allowClose;
this.closeIcon = config.closeIcon;
this.enabled = config.enabled ?? true;
this.activated = config.activated;
this.backgroundColor = config.backgroundColor;
this.activatedBackgroundColor = config.activatedBackgroundColor;
this.borderRadius = config.borderRadius;
this.size = config.size ?? ChipV2Size.NORMAL;
this.direction = config.direction ?? Direction.Auto;
this.accessibilityDescription = config.accessibilityDescription;
this.accessibilityLevel = config.accessibilityLevel;
this.accessibilitySelectedType = config.accessibilitySelectedType;
this.maxFontScale = config.maxFontScale;
this.minFontScale = config.minFontScale;
this.padding = config.padding;
this.fontSize = config.fontSize;
this.backgroundSystemMaterial = config.backgroundSystemMaterial;
this.activatedBackgroundSystemMaterial = config.activatedBackgroundSystemMaterial;
this.onClose = config.onClose;
this.onClicked = config.onClicked;
}
}
@ComponentV2
export struct ChipV2 {
private theme: ChipTheme = {
prefixIcon: {
normalSize: {
width: $r('sys.float.chip_normal_icon_size'),
height: $r('sys.float.chip_normal_icon_size')
},
smallSize: {
width: $r('sys.float.chip_small_icon_size'),
height: $r('sys.float.chip_small_icon_size')
},
fillColor: $r('sys.color.chip_usually_icon_color'),
activatedFillColor: $r('sys.color.chip_active_icon_color'),
focusFillColor: $r('sys.color.chip_icon_focus_fill'),
focusActivatedColor: $r('sys.color.chip_icon_activated_focus_color'),
},
label: {
normalFontSize: $r('sys.float.chip_normal_font_size'),
smallFontSize: $r('sys.float.chip_small_font_size'),
adaptiveItemFontSize: $r('sys.float.Caption_M'),
focusFontColor: $r('sys.color.chip_focus_text'),
focusActiveFontColor: $r('sys.color.chip_activated_focus_font_color'),
fontColor: $r('sys.color.chip_font_color'),
activatedFontColor: $r('sys.color.chip_activated_fontcolor'),
fontFamily: 'HarmonyOS Sans',
fontWeight: $r('sys.float.chip_text_font_weight'),
activatedFontWeight: $r('sys.float.chip_activated_text_font_weight'),
normalMargin: {
left: 6,
right: 6,
top: 0,
bottom: 0
},
smallMargin: {
left: 4,
right: 4,
top: 0,
bottom: 0
},
defaultFontSize: 14,
localizedNormalMargin: {
start: LengthMetricsCache.get('sys.float.chip_normal_text_margin', LengthMetrics.vp(6)),
end: LengthMetricsCache.get('sys.float.chip_normal_text_margin', LengthMetrics.vp(6)),
top: LengthMetrics.vp(0),
bottom: LengthMetrics.vp(0)
},
localizedSmallMargin: {
start: LengthMetricsCache.get('sys.float.chip_small_text_margin', LengthMetrics.vp(4)),
end: LengthMetricsCache.get('sys.float.chip_small_text_margin', LengthMetrics.vp(4)),
top: LengthMetrics.vp(0),
bottom: LengthMetrics.vp(0),
}
},
suffixIcon: {
normalSize: {
width: $r('sys.float.chip_normal_icon_size'),
height: $r('sys.float.chip_normal_icon_size')
},
smallSize: {
width: $r('sys.float.chip_small_icon_size'),
height: $r('sys.float.chip_small_icon_size')
},
fillColor: $r('sys.color.chip_usually_icon_color'),
activatedFillColor: $r('sys.color.chip_active_icon_color'),
focusFillColor: $r('sys.color.chip_icon_focus_fill'),
focusActivatedColor: $r('sys.color.chip_icon_activated_focus_color'),
defaultDeleteIcon: $r('sys.media.ohos_ic_public_cancel', 16, 16),
focusable: false,
isShowMargin: $r('sys.float.chip_show_close_icon_margin'),
},
defaultSymbol: {
normalFontColor: [$r('sys.color.chip_usually_icon_color')],
activatedFontColor: [$r('sys.color.chip_active_icon_color')],
normalSymbolFontSize:
LengthMetricsCache.get('sys.float.chip_normal_icon_size', LengthMetrics.vp(16)).value as Length,
smallSymbolFontSize:
LengthMetricsCache.get('sys.float.chip_small_icon_size', LengthMetrics.vp(16)).value as Length,
defaultEffect: -1,
},
chipNode: {
suitAgeScale: 1.75,
minLabelWidth: 12,
normalHeight: $r('sys.float.chip_normal_height'),
smallHeight: $r('sys.float.chip_small_height'),
activatedNormalHeight: $r('sys.float.chip_activated_normal_height'),
activatedSmallHeight: $r('sys.float.chip_activated_small_height'),
enabled: true,
activated: false,
backgroundColor: $r('sys.color.chip_background_color'),
activatedBackgroundColor: $r('sys.color.chip_container_activated_color'),
focusOutlineColor: $r('sys.color.ohos_id_color_focused_outline'),
focusOutlineMargin: 2,
borderColor: $r('sys.color.chip_border_color'),
defaultBorderWidth: $r('sys.float.chip_border_width'),
activatedBorderColor: $r('sys.color.chip_activated_border_color'),
normalBorderRadius: $r('sys.float.chip_border_radius_normal'),
smallBorderRadius: $r('sys.float.chip_border_radius_small'),
activatedBorderWidth: $r('sys.float.chip_activated_border_width'),
borderWidth: 2,
focusBtnScaleX: $r('sys.float.chip_focused_btn_scale'),
focusBtnScaleY: $r('sys.float.chip_focused_btn_scale'),
localizedNormalPadding: {
start: LengthMetricsCache.get('sys.float.chip_normal_text_padding', LengthMetrics.vp(16)),
end: LengthMetricsCache.get('sys.float.chip_normal_text_padding', LengthMetrics.vp(16)),
top: LengthMetrics.vp(4),
bottom: LengthMetrics.vp(4)
},
localizedSmallPadding: {
start: LengthMetricsCache.get('sys.float.chip_small_text_padding', LengthMetrics.vp(12)),
end: LengthMetricsCache.get('sys.float.chip_small_text_padding', LengthMetrics.vp(12)),
top: LengthMetrics.vp(4),
bottom: LengthMetrics.vp(4)
},
localizedActivatedNormalPadding: {
start: LengthMetricsCache.get('sys.float.chip_activated_normal_text_padding', LengthMetrics.vp(16)),
end: LengthMetricsCache.get('sys.float.chip_activated_normal_text_padding', LengthMetrics.vp(16)),
top: LengthMetrics.vp(4),
bottom: LengthMetrics.vp(4)
},
localizedActivatedSmallPadding: {
start: LengthMetricsCache.get('sys.float.chip_activated_small_text_padding', LengthMetrics.vp(12)),
end: LengthMetricsCache.get('sys.float.chip_activated_small_text_padding', LengthMetrics.vp(12)),
top: LengthMetrics.vp(4),
bottom: LengthMetrics.vp(4)
},
hoverBlendColor: $r('sys.color.chip_hover_color'),
pressedBlendColor: $r('sys.color.chip_press_color'),
focusBgColor: $r('sys.color.chip_focus_color'),
focusActivatedBgColor: $r('sys.color.chip_container_activated_focus_color'),
opacity: { normal: 1, hover: 0.95, pressed: 0.9 },
normalShadowStyle: $r('sys.float.chip_normal_shadow_style'),
smallShadowStyle: $r('sys.float.chip_small_shadow_style'),
breakPointConstraintWidth: {
breakPointMinWidth: 128,
breakPointSmMaxWidth: 156,
breakPointMdMaxWidth: 280,
breakPointLgMaxWidth: 400
},
}
};
@Require @Param chipV2Options: ChipV2Options;
@Event onClose?: VoidCallback;
@Event onClicked?: Callback<void>;
@Local isChipExist: boolean = true;
@Local chipScale: ScaleOptions = { x: 1, y: 1 };
@Local chipOpacity: number = 1;
@Local chipNodeInFocus: boolean = false;
@Local breakPoint: BreakPointsType = BreakPointsType.SM;
@Local fontSizeScale: number = 1;
@Local useAdaptiveLineHeight: boolean = false;
private smListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(0vp<width) and (width<600vp)');
private mdListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(600vp<=width) and (width<840vp)');
private lgListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(840vp<=width)');
private symbolEffect: SymbolEffect = new SymbolEffect();
private environmentCallbackID?: number = undefined;
private environmentCallback: EnvironmentCallback = {
onConfigurationUpdated: (configuration) => {
this.fontSizeScale = configuration.fontSizeScale ?? 1;
this.updateLanguageLineHeight();
},
onMemoryLevel() {
}
};
private isSuffixIconFocusStyleCustomized = this.resourceToNumber(this.theme.suffixIcon.isShowMargin, 0) !== 0;
private isSuffixIconFocusable = this.resourceToNumber(this.theme.suffixIcon.isShowMargin, 0) !== 1;
updateLanguageLineHeight(): void {
if (deviceInfo.sdkApiVersion < 26) {
return;
}
const resourceManager = this.getUIContext().getHostContext()?.resourceManager;
if (!resourceManager) {
console.error(`[ChipV2] failed to get resourceManager`);
return;
}
try {
this.useAdaptiveLineHeight = resourceManager!.getStringByNameSync('text_fallback_line_spacing') === 'true';
} catch (e) {
console.error(`[ChipV2] failed to get text_fallback_line_spacing resource`);
}
}
aboutToAppear(): void {
this.smListener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => {
if (mediaQueryResult.matches) {
this.breakPoint = BreakPointsType.SM;
}
});
this.mdListener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => {
if (mediaQueryResult.matches) {
this.breakPoint = BreakPointsType.MD;
}
});
this.lgListener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => {
if (mediaQueryResult.matches) {
this.breakPoint = BreakPointsType.LG;
}
});
this.updateLanguageLineHeight();
let abilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext | undefined;
if (abilityContext) {
this.fontSizeScale = abilityContext.config?.fontSizeScale ?? 1;
this.environmentCallbackID = abilityContext.getApplicationContext().on('environment', this.environmentCallback);
}
this.onClose = this.chipV2Options.onClose;
this.onClicked = this.chipV2Options.onClicked;
}
aboutToDisappear(): void {
this.smListener.off('change');
this.mdListener.off('change');
this.lgListener.off('change');
if (this.environmentCallbackID) {
this.getUIContext()?.getHostContext()?.getApplicationContext().off('environment', this.environmentCallbackID);
this.environmentCallbackID = void 0;
}
}
private isSetActiveChipBgColor(): boolean {
if (!this.chipV2Options.activatedBackgroundColor) {
return false;
}
try {
return this.chipV2Options.activatedBackgroundColor.color !==
ColorMetrics.resourceColor(this.theme.chipNode.activatedBackgroundColor).color;
} catch (error) {
console.error(`[ChipV2] failed to get ColorMetrics.resourceColor`);
return false;
}
}
private isSetNormalChipBgColor(): boolean {
if (!this.chipV2Options.backgroundColor) {
return false;
}
try {
return this.chipV2Options.backgroundColor.color !==
ColorMetrics.resourceColor(this.theme.chipNode.backgroundColor).color;
} catch (error) {
console.error(`[ChipV2] failed to get resourceColor`);
return false;
}
}
private getShadowStyles(): ShadowStyle | undefined {
if (!this.chipNodeInFocus) {
return undefined;
}
return this.resourceToNumber(this.isSmallChipSize() ? this.theme.chipNode.smallShadowStyle :
this.theme.chipNode.normalShadowStyle, -1);
}
@Builder
ChipBuilder() {
Button({ type: ButtonType.Normal }) {
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
if (this.hasPrefixSymbolIcon()) {
SymbolGlyph()
.fontSize(this.getFontSizeForSymbol())
.maxFontScale(this.chipV2Options.maxFontScale)
.minFontScale(this.chipV2Options.minFontScale)
.fontColor(this.getDefaultSymbolColor(IconType.PREFIX_SYMBOL))
.flexShrink(0)
.attributeModifier(this.getPrefixSymbolModifier())
.effectStrategy(SymbolEffectStrategy.NONE)
.symbolEffect(this.symbolEffect, false)
.symbolEffect(this.symbolEffect, this.theme.defaultSymbol.defaultEffect)
} else if (this.hasPrefixImageIcon()) {
Image(this.getPrefixImageIcon()!.src)
.direction(this.chipV2Options.direction)
.size(this.getPrefixIconSize())
.fillColor(this.getPrefixIconFilledColor())
.objectFit(ImageFit.Cover)
.focusable(false)
.flexShrink(0)
.draggable(false)
.attributeModifier(this.getPrefixImageIcon()!.modifier)
}
Text(this.chipV2Options.label.text)
.draggable(false)
.flexShrink(1)
.focusable(true)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.textAlign(TextAlign.Center)
.direction(this.chipV2Options.direction)
.fontSize(this.getLabelFontSize())
.fontColor(this.getLabelFontColor())
.fontFamily(this.getLabelFontFamily())
.fontWeight(this.getLabelFontWeight())
.maxFontScale(this.chipV2Options.maxFontScale)
.minFontScale(this.chipV2Options.minFontScale)
.margin(this.getLabelMargin())
.includeFontPadding(this.useAdaptiveLineHeight)
.fallbackLineSpacing(this.useAdaptiveLineHeight)
.attributeModifier(this.chipV2Options.label.modifier)
if (this.hasSuffixSymbolIcon()) {
Button({ type: ButtonType.Normal }) {
SymbolGlyph()
.fontSize(this.getFontSizeForSymbol())
.maxFontScale(this.chipV2Options.maxFontScale)
.minFontScale(this.chipV2Options.minFontScale)
.fontColor(this.getDefaultSymbolColor(IconType.SUFFIX_SYMBOL))
.attributeModifier(this.getSuffixSymbolModifier())
.effectStrategy(SymbolEffectStrategy.NONE)
.symbolEffect(this.symbolEffect, false)
.symbolEffect(this.symbolEffect, this.theme.defaultSymbol.defaultEffect)
}
.onClick(this.getSuffixSymbolAction())
.accessibilityText(this.getSuffixSymbolAccessibilityText())
.accessibilityDescription(this.getSuffixSymbolAccessibilityDescription())
.accessibilityLevel(this.getSuffixSymbolAccessibilityLevel())
.flexShrink(0)
.backgroundColor(Color.Transparent)
.borderRadius(0)
.padding(0)
.stateEffect(false)
.hoverEffect(HoverEffect.None)
.focusable(this.isSuffixIconFocusable)
} else if (this.hasSuffixImageIcon()) {
Button({ type: ButtonType.Normal }) {
Image(this.getSuffixImageIcon()!.src)
.direction(this.chipV2Options.direction)
.size(this.getSuffixIconSize())
.fillColor(this.getSuffixIconFilledColor())
.objectFit(ImageFit.Cover)
.draggable(false)
.attributeModifier(this.getSuffixImageIcon()!.modifier)
}
.backgroundColor(Color.Transparent)
.borderRadius(0)
.padding(0)
.flexShrink(0)
.stateEffect(false)
.hoverEffect(HoverEffect.None)
.size(this.getSuffixIconSize())
.accessibilityText(this.getSuffixImageIconAccessibilityText())
.accessibilityDescription(this.getSuffixImageIconAccessibilityDescription())
.accessibilityLevel(this.getSuffixImageIconAccessibilityLevel())
.onClick(this.getSuffixIconAction())
.focusable(this.isSuffixIconFocusable)
} else if (this.isClosable()) {
Button({ type: ButtonType.Normal }) {
SymbolGlyph($r('sys.symbol.xmark'))
.fontSize(this.getCloseOptionsFontsize())
.maxFontScale(this.chipV2Options.maxFontScale)
.minFontScale(this.chipV2Options.minFontScale)
.fontColor(this.getDefaultSymbolColor(IconType.SUFFIX_SYMBOL))
}
.backgroundColor(Color.Transparent)
.borderRadius(0)
.padding(0)
.flexShrink(0)
.stateEffect(false)
.hoverEffect(HoverEffect.None)
.accessibilityText(this.getCloseIconAccessibilityText())
.accessibilityDescription(this.getCloseIconAccessibilityDescription())
.accessibilityLevel(this.getCloseIconAccessibilityLevel())
.responseRegion({
x: $r('sys.float.chip_touch_hot_zone_x'),
y: $r('sys.float.chip_touch_hot_zone_y'),
width: $r('sys.float.chip_touch_hot_zone_width'),
height: $r('sys.float.chip_touch_hot_zone_height')
})
.onClick(() => {
if (!this.isChipEnabled()) {
return;
}
this.onClose?.();
this.deleteChip();
})
.focusable(this.isSuffixIconFocusable)
}
}
.direction(this.chipV2Options.direction)
.padding(this.getChipPadding())
.size(this.getChipSize())
.constraintSize(this.getChipConstraintSize())
}
.clip(false)
.shadow(this.getShadowStyles())
.padding(0)
.focusable(true)
.size(this.getChipSize())
.enabled(this.isChipEnabled())
.direction(this.chipV2Options.direction)
.backgroundColor(this.getChipBackgroundColor())
.systemMaterial(this.getBackgroundSystemMaterial())
.borderWidth(this.getChipNodeBorderWidth())
.borderColor(this.getChipNodeBorderColor())
.borderRadius(this.getChipBorderRadius())
.responseRegion(this.getChipResponseRegion())
.scale(this.chipScale)
.opacity(this.chipOpacity)
.accessibilityGroup(true)
.accessibilityDescription(this.getAccessibilityDescription())
.accessibilityLevel(this.chipV2Options.accessibilityLevel)
.accessibilityChecked(this.getAccessibilityChecked())
.accessibilitySelected(this.getAccessibilitySelected())
.onClick(this.getChipOnClicked())
.onKeyEvent((event) => {
if (!event || event.type === null || event.type !== KeyType.Down) {
return;
}
let isDeleteChip = event.keyCode === KeyCode.KEYCODE_FORWARD_DEL;
let isEnterDeleteChip = event.keyCode === KeyCode.KEYCODE_ENTER && this.chipV2Options.allowClose !== false &&
!this.hasSuffixImageIcon() && this.isSuffixIconFocusStyleCustomized;
if (isDeleteChip || isEnterDeleteChip) {
this.deleteChip();
}
})
.onFocus(() => {
if (this.isSuffixIconFocusStyleCustomized) {
this.chipNodeInFocus = true;
}
this.chipZoomIn();
})
.onBlur(() => {
if (this.isSuffixIconFocusStyleCustomized) {
this.chipNodeInFocus = false;
}
this.chipZoomOut();
})
.hoverStyle()
}
@Styles
hoverStyle() {
.onHover(!this.isSuffixIconFocusStyleCustomized ? undefined : (isHover: boolean) => {
isHover ? this.chipZoomIn() : this.chipZoomOut();
})
}
private hasPrefixImageIcon(): boolean {
return this.chipV2Options.prefixIcon instanceof ChipV2PrefixImageIcon;
}
private hasPrefixSymbolIcon(): boolean {
return this.chipV2Options.prefixIcon instanceof ChipV2PrefixSymbolIcon &&
!!((this.chipV2Options.prefixIcon as ChipV2PrefixSymbolIcon).normal ||
(this.chipV2Options.prefixIcon as ChipV2PrefixSymbolIcon).activated);
}
private hasSuffixImageIcon(): boolean {
return this.chipV2Options.suffixIcon instanceof ChipV2SuffixImageIcon;
}
private hasSuffixSymbolIcon(): boolean {
return this.chipV2Options.suffixIcon instanceof ChipV2SuffixSymbolIcon &&
!!((this.chipV2Options.suffixIcon as ChipV2SuffixSymbolIcon).normal ||
(this.chipV2Options.suffixIcon as ChipV2SuffixSymbolIcon).activated);
}
private getPrefixImageIcon(): ChipV2PrefixImageIcon | undefined {
return this.hasPrefixImageIcon() ? this.chipV2Options.prefixIcon as ChipV2PrefixImageIcon : undefined;
}
private getSuffixImageIcon(): ChipV2SuffixImageIcon | undefined {
return this.hasSuffixImageIcon() ? this.chipV2Options.suffixIcon as ChipV2SuffixImageIcon : undefined;
}
private getSuffixSymbolIcon(): ChipV2SuffixSymbolIcon | undefined {
return this.hasSuffixSymbolIcon() ? this.chipV2Options.suffixIcon as ChipV2SuffixSymbolIcon : undefined;
}
private getCloseIconAccessibilityLevel(): string {
if (this.chipV2Options.closeIcon?.accessibilityLevel === 'no' ||
this.chipV2Options.closeIcon?.accessibilityLevel === 'no-hide-descendants') {
return this.chipV2Options.closeIcon.accessibilityLevel;
}
return 'yes';
}
private getCloseIconAccessibilityDescription(): Resource | undefined {
if (typeof this.chipV2Options.closeIcon?.accessibilityDescription === 'undefined') {
return void 0;
}
return this.chipV2Options.closeIcon.accessibilityDescription as Resource;
}
private getCloseIconAccessibilityText(): Resource {
if (typeof this.chipV2Options.closeIcon?.accessibilityText === 'undefined') {
return $r('sys.string.delete_used_for_accessibility_text');
}
return this.chipV2Options.closeIcon.accessibilityText as ESObject as Resource;
}
getSuffixIconAction(): Callback<ClickEvent> | undefined {
if (this.hasSuffixImageIcon()) {
const suffixImgIcon = this.getSuffixImageIcon()!;
if (!suffixImgIcon.action) {
return void 0;
}
return () => {
if (this.isChipEnabled()) {
suffixImgIcon.action?.();
}
};
}
return void 0;
}
getSuffixIconFilledColor(): ResourceColor | ColorMetrics {
if (this.isChipActivated()) {
return this.getSuffixImageIcon()?.activatedFillColor ?? this.getDefaultActiveIconColor(IconType.PREFIX_ICON);
}
return this.getSuffixImageIcon()?.fillColor ?? this.getDefaultFillIconColor(IconType.SUFFIX_ICON);
}
getSuffixIconSize(): SizeOptions {
let suffixIconSize: SizeOptions = { width: 0, height: 0 };
let width: LengthMetrics | undefined = this.getSuffixImageIcon()?.size?.width;
if (typeof width !== 'undefined' && this.isValidLength(width)) {
suffixIconSize.width = lengthMetricsToLength(width);
} else {
suffixIconSize.width =
this.isSmallChipSize() ? this.theme.suffixIcon.smallSize.width : this.theme.suffixIcon.normalSize.width;
}
let height: LengthMetrics | undefined = this.getSuffixImageIcon()?.size?.height;
if (typeof height !== 'undefined' && this.isValidLength(height)) {
suffixIconSize.height = lengthMetricsToLength(height);
} else {
suffixIconSize.height =
this.isSmallChipSize() ? this.theme.suffixIcon.smallSize.height : this.theme.suffixIcon.normalSize.height;
}
return suffixIconSize;
}
getSuffixImageIconAccessibilityLevel(): string {
const suffixIcon = this.getSuffixImageIcon();
if (suffixIcon?.accessibilityLevel === 'no' || suffixIcon?.accessibilityLevel === 'no-hide-descendants') {
return suffixIcon.accessibilityLevel;
}
return suffixIcon?.action ? 'yes' : 'no';
}
getSuffixImageIconAccessibilityDescription(): Resource | undefined {
const suffixIcon = this.getSuffixImageIcon();
if (typeof suffixIcon?.accessibilityDescription === 'undefined') {
return void 0;
}
return suffixIcon.accessibilityDescription as ESObject as Resource;
}
getSuffixImageIconAccessibilityText(): Resource | undefined {
const suffixIcon = this.getSuffixImageIcon();
if (typeof suffixIcon?.accessibilityText === 'undefined') {
return void 0;
}
return suffixIcon.accessibilityText as ESObject as Resource;
}
isClosable(): boolean {
return this.chipV2Options.allowClose ?? true;
}
getSuffixSymbolModifier(): SymbolGlyphModifier | undefined {
if (this.isChipActivated()) {
return this.getSuffixSymbolIcon()?.activated;
}
return this.getSuffixSymbolIcon()?.normal;
}
getSuffixSymbolAccessibilityLevel(): string {
if (this.isChipActivated()) {
if (this.getSuffixSymbolIcon()?.activatedAccessibility?.accessibilityLevel === 'no' ||
this.getSuffixSymbolIcon()?.activatedAccessibility?.accessibilityLevel === 'no-hide-descendants') {
return this.getSuffixSymbolIcon()!.activatedAccessibility!.accessibilityLevel!;
}
return this.getSuffixSymbolIcon()?.action ? 'yes' : 'no';
}
if (this.getSuffixSymbolIcon()?.normalAccessibility?.accessibilityLevel === 'no' ||
this.getSuffixSymbolIcon()?.normalAccessibility?.accessibilityLevel === 'no-hide-descendants') {
return this.getSuffixSymbolIcon()!.normalAccessibility!.accessibilityLevel!;
}
return this.getSuffixSymbolIcon()?.action ? 'yes' : 'no';
}
getSuffixSymbolAccessibilityDescription(): Resource | undefined {
if (this.isChipActivated()) {
if (typeof this.getSuffixSymbolIcon()?.activatedAccessibility?.accessibilityDescription !== 'undefined') {
return this.getSuffixSymbolIcon()!.activatedAccessibility!.accessibilityDescription as Resource;
}
return void 0;
}
if (typeof this.getSuffixSymbolIcon()?.normalAccessibility?.accessibilityDescription !== 'undefined') {
return this.getSuffixSymbolIcon()!.normalAccessibility!.accessibilityDescription as Resource;
}
return void 0;
}
getSuffixSymbolAccessibilityText(): Resource | undefined {
if (this.isChipActivated()) {
if (typeof this.getSuffixSymbolIcon()?.activatedAccessibility?.accessibilityText !== 'undefined') {
return this.getSuffixSymbolIcon()!.activatedAccessibility!.accessibilityText as Resource;
}
return void 0;
}
if (typeof this.getSuffixSymbolIcon()?.normalAccessibility?.accessibilityText !== 'undefined') {
return this.getSuffixSymbolIcon()!.normalAccessibility!.accessibilityText as Resource;
}
return void 0;
}
getSuffixSymbolAction(): Callback<ClickEvent> | undefined {
if (typeof this.getSuffixSymbolIcon()?.action === 'undefined') {
return void 0;
}
return () => {
if (!this.isChipEnabled()) {
return;
}
this.getSuffixSymbolIcon()?.action?.();
};
}
getPrefixIconFilledColor(): ResourceColor | ColorMetrics {
if (this.isChipActivated()) {
return this.getPrefixImageIcon()?.activatedFillColor ?? this.getDefaultActiveIconColor(IconType.PREFIX_ICON);
}
return this.getPrefixImageIcon()?.fillColor ?? this.getDefaultFillIconColor(IconType.PREFIX_ICON);
}
getPrefixIconSize(): SizeOptions {
let prefixIconSize: SizeOptions = { width: 0, height: 0 };
let width: LengthMetrics | undefined = this.getPrefixImageIcon()?.size?.width;
if (typeof width !== 'undefined' && this.isValidLength(width)) {
prefixIconSize.width = lengthMetricsToLength(width);
} else {
prefixIconSize.width =
this.isSmallChipSize() ? this.theme.prefixIcon.smallSize.width : this.theme.prefixIcon.normalSize.width;
}
let height: LengthMetrics | undefined = this.getPrefixImageIcon()?.size?.height;
if (typeof height !== 'undefined' && this.isValidLength(height)) {
prefixIconSize.height = lengthMetricsToLength(height);
} else {
prefixIconSize.height =
this.isSmallChipSize() ? this.theme.prefixIcon.smallSize.height : this.theme.prefixIcon.normalSize.height;
}
return prefixIconSize;
}
getPrefixSymbolModifier(): SymbolGlyphModifier | undefined {
if (!(this.chipV2Options.prefixIcon instanceof ChipV2PrefixSymbolIcon)) {
return undefined;
}
const prefixSymbol = this.chipV2Options.prefixIcon as ChipV2PrefixSymbolIcon;
if (this.isChipActivated()) {
return prefixSymbol.activated;
}
return prefixSymbol.normal;
}
getDefaultSymbolColor(iconType: string): ResourceColor[] {
return this.isChipActivated() ? this.getSymbolActiveColor(iconType) :
this.getSymbolFillColor(iconType);
}
private getDefaultActiveIconColor(iconType: string): ResourceColor {
if (iconType === IconType.PREFIX_ICON) {
return this.chipNodeInFocus ? this.theme.prefixIcon.focusActivatedColor :
this.theme.prefixIcon.activatedFillColor;
} else {
return this.chipNodeInFocus ? this.theme.suffixIcon.focusActivatedColor :
this.theme.suffixIcon.activatedFillColor;
}
}
private getDefaultFillIconColor(iconType: string): ResourceColor {
if (iconType === IconType.PREFIX_ICON) {
return this.chipNodeInFocus ? this.theme.prefixIcon.focusFillColor : this.theme.prefixIcon.fillColor;
} else {
return this.chipNodeInFocus ? this.theme.suffixIcon.focusFillColor : this.theme.suffixIcon.fillColor;
}
}
private getSymbolActiveColor(iconType: string): ResourceColor[] {
if (!this.chipNodeInFocus) {
return this.theme.defaultSymbol.activatedFontColor;
}
if (iconType === IconType.PREFIX_SYMBOL) {
return [this.theme.prefixIcon.focusActivatedColor];
}
if (iconType === IconType.SUFFIX_SYMBOL) {
return [this.theme.suffixIcon.focusActivatedColor];
}
return this.theme.defaultSymbol.activatedFontColor;
}
private getSymbolFillColor(iconType?: string): ResourceColor[] {
if (!this.chipNodeInFocus) {
return this.theme.defaultSymbol.normalFontColor;
}
if (iconType === IconType.PREFIX_SYMBOL) {
return [this.theme.prefixIcon.focusFillColor];
}
if (iconType === IconType.SUFFIX_SYMBOL) {
return [this.theme.suffixIcon.focusFillColor];
}
return this.theme.defaultSymbol.normalFontColor;
}
getChipConstraintSize(): ConstraintSizeOptions | undefined {
const constraintSize: ConstraintSizeOptions = {};
if (typeof this.chipV2Options.size === 'string') {
constraintSize.maxWidth = this.getChipMaxWidth();
if (this.chipV2Options.size === ChipV2Size.SMALL) {
constraintSize.minHeight =
this.isChipActivated() ? this.theme.chipNode.activatedSmallHeight : this.theme.chipNode.smallHeight;
} else {
constraintSize.minHeight =
this.isChipActivated() ? this.theme.chipNode.activatedNormalHeight : this.theme.chipNode.normalHeight;
}
} else {
if (typeof this.chipV2Options.size?.width === 'undefined' ||
!this.isValidLength(this.chipV2Options.size.width)) {
constraintSize.maxWidth = this.getChipMaxWidth();
}
if (typeof this.chipV2Options.size?.height === 'undefined' ||
!this.isValidLength(this.chipV2Options.size.height)) {
constraintSize.minHeight =
this.isChipActivated() ? this.theme.chipNode.activatedNormalHeight : this.theme.chipNode.normalHeight;
}
}
return constraintSize;
}
private getChipHeight(): number {
let height: Length;
if (typeof this.chipV2Options.size !== 'string') {
if (typeof this.chipV2Options.size?.height !== 'undefined' &&
this.isValidLength(this.chipV2Options.size.height)) {
height = lengthMetricsToLength(this.chipV2Options.size.height);
} else {
height = this.isChipActivated() ? this.theme.chipNode.activatedNormalHeight : this.theme.chipNode.normalHeight;
}
} else if (this.chipV2Options.size === ChipV2Size.SMALL) {
height = this.isChipActivated() ? this.theme.chipNode.activatedSmallHeight : this.theme.chipNode.smallHeight;
} else {
height = this.isChipActivated() ? this.theme.chipNode.activatedNormalHeight : this.theme.chipNode.normalHeight;
}
return this.parseLength(height) ?? HOT_SPOT_MIN_HEIGHT;
}
private getChipResponseRegion(): Rectangle | undefined {
if (deviceInfo.sdkApiVersion >= 26) {
const chipHeight = this.getChipHeight();
if (chipHeight < HOT_SPOT_MIN_HEIGHT) {
return {
x: 0,
y: (chipHeight - HOT_SPOT_MIN_HEIGHT) / 2,
width: '100%',
height: HOT_SPOT_MIN_HEIGHT
};
}
}
return undefined;
}
getChipMaxWidth(): Length | undefined {
if (this.fontSizeScale >= this.theme.chipNode.suitAgeScale) {
return void 0;
}
if (this.breakPoint === BreakPointsType.SM) {
return this.theme.chipNode.breakPointConstraintWidth.breakPointSmMaxWidth;
}
if (this.breakPoint === BreakPointsType.MD) {
return this.theme.chipNode.breakPointConstraintWidth.breakPointMdMaxWidth;
}
if (this.breakPoint === BreakPointsType.LG) {
return this.theme.chipNode.breakPointConstraintWidth.breakPointLgMaxWidth;
}
return void 0;
}
getChipSize(): SizeOptions | undefined {
const chipSize: SizeOptions = {
width: 'auto',
height: 'auto'
};
if (typeof this.chipV2Options.size !== 'string') {
if (typeof this.chipV2Options.size?.width !== 'undefined' &&
this.isValidLength(this.chipV2Options.size.width)) {
chipSize.width = lengthMetricsToLength(this.chipV2Options.size.width);
}
if (typeof this.chipV2Options.size?.height !== 'undefined' &&
this.isValidLength(this.chipV2Options.size.height)) {
chipSize.height = lengthMetricsToLength(this.chipV2Options.size.height);
}
}
return chipSize;
}
copyPadding(src: LocalizedPadding): LocalizedPadding {
return {
top: src.top,
bottom: src.bottom,
start: src.start,
end: src.end
}
}
getChipPadding(): Length | Padding | LocalizedPadding {
let chipTheme = this.theme.chipNode;
let res: LocalizedPadding;
if (this.isSmallChipSize()) {
res = this.isChipActivated() ? this.copyPadding(chipTheme.localizedActivatedSmallPadding) :
this.copyPadding(chipTheme.localizedSmallPadding);
} else {
res = this.isChipActivated() ? this.copyPadding(chipTheme.localizedActivatedNormalPadding) :
this.copyPadding(chipTheme.localizedNormalPadding);
}
if (this.chipV2Options.padding?.top &&
LengthMetricsUtils.getInstance().isNaturalNumber(this.chipV2Options.padding.top)) {
res.top = this.chipV2Options.padding.top;
}
if (this.chipV2Options.padding?.bottom &&
LengthMetricsUtils.getInstance().isNaturalNumber(this.chipV2Options.padding.bottom)) {
res.bottom = this.chipV2Options.padding.bottom;
}
if (this.chipV2Options.padding?.start &&
LengthMetricsUtils.getInstance().isNaturalNumber(this.chipV2Options.padding.start)) {
res.start = this.chipV2Options.padding.start;
}
if (this.chipV2Options.padding?.end &&
LengthMetricsUtils.getInstance().isNaturalNumber(this.chipV2Options.padding.end)) {
res.end = this.chipV2Options.padding.end;
}
return res;
}
getLabelMargin(): Length | Padding | LocalizedPadding {
const localizedLabelMargin: LocalizedMargin = {
start: LengthMetrics.vp(0),
end: LengthMetrics.vp(0),
};
const defaultLocalizedMargin =
this.isSmallChipSize() ? this.theme.label.localizedSmallMargin : this.theme.label.localizedNormalMargin;
if (typeof this.chipV2Options.label.localizedLabelMargin?.start !== 'undefined' &&
this.chipV2Options.label.localizedLabelMargin.start.value >= 0) {
localizedLabelMargin.start = this.chipV2Options.label.localizedLabelMargin.start;
} else if (this.hasPrefix()) {
localizedLabelMargin.start = defaultLocalizedMargin.start;
}
if (typeof this.chipV2Options.label.localizedLabelMargin?.end !== 'undefined' &&
this.chipV2Options.label.localizedLabelMargin.end.value >= 0) {
localizedLabelMargin.end = this.chipV2Options.label.localizedLabelMargin.end;
} else if (this.hasSuffix() || this.isNeedShowCloseIconMargin()) {
localizedLabelMargin.end = defaultLocalizedMargin.end;
}
if (typeof this.chipV2Options.label.localizedLabelMargin === 'object') {
return localizedLabelMargin;
}
if (typeof this.chipV2Options.label.labelMargin === 'object') {
const labelMargin: Margin = { left: 0, right: 0 };
const defaultLabelMargin: Margin =
this.isSmallChipSize() ? this.theme.label.smallMargin : this.theme.label.normalMargin;
if (typeof this.chipV2Options.label.labelMargin?.left !== 'undefined' &&
this.isValidLength(this.chipV2Options.label.labelMargin.left)) {
labelMargin.left = lengthMetricsToLength(this.chipV2Options.label.labelMargin.left);
} else if (this.hasPrefix()) {
labelMargin.left = defaultLabelMargin.left;
}
if (typeof this.chipV2Options.label.labelMargin?.right !== 'undefined' &&
this.isValidLength(this.chipV2Options.label.labelMargin.right)) {
labelMargin.right = lengthMetricsToLength(this.chipV2Options.label.labelMargin.right);
} else if (this.hasSuffix()) {
labelMargin.right = defaultLabelMargin.right;
}
return labelMargin;
}
return localizedLabelMargin;
}
hasSuffix(): boolean {
if (this.hasSuffixImageIcon()) {
return true;
}
return this.isChipActivated() ? !!(this.chipV2Options.suffixIcon as ChipV2SuffixSymbolIcon)?.activated :
!!(this.chipV2Options.suffixIcon as ChipV2SuffixSymbolIcon)?.normal;
}
private hasPrefix(): boolean {
if (this.hasPrefixImageIcon()) {
return true;
}
return this.isChipActivated() ? !!(this.chipV2Options.prefixIcon as ChipV2PrefixSymbolIcon)?.activated :
!!(this.chipV2Options.prefixIcon as ChipV2PrefixSymbolIcon)?.normal;
}
getLabelFontWeight(): string | number | FontWeight {
if (this.isChipActivated()) {
return this.resourceToNumber(this.theme.label.activatedFontWeight, FontWeight.Medium) as FontWeight;
}
return this.resourceToNumber(this.theme.label.fontWeight, FontWeight.Regular) as FontWeight;
}
getLabelFontFamily(): ResourceStr {
return this.chipV2Options.label.fontFamily ?? this.theme.label.fontFamily;
}
private getFontSizeForSymbol(): Length | Dimension {
if (!!this.chipV2Options.fontSize && this.isValidLength(this.chipV2Options.fontSize)) {
return lengthMetricsToLength(this.chipV2Options.fontSize);
}
return this.isSmallChipSize() ? this.theme.defaultSymbol.smallSymbolFontSize :
this.theme.defaultSymbol.normalSymbolFontSize;
}
private getCloseOptionsFontsize(): Length | Dimension {
if (!!this.chipV2Options.closeIcon?.fontSize && this.isValidLength(this.chipV2Options.closeIcon.fontSize)) {
return lengthMetricsToLength(this.chipV2Options.closeIcon.fontSize);
}
if (!!this.chipV2Options.fontSize && this.isValidLength(this.chipV2Options.fontSize)) {
return lengthMetricsToLength(this.chipV2Options.fontSize);
}
return this.isSmallChipSize() ? this.theme.defaultSymbol.smallSymbolFontSize :
this.theme.defaultSymbol.normalSymbolFontSize;
}
private getActiveFontColor(): ResourceColor {
return this.chipNodeInFocus ? this.theme.label.focusActiveFontColor : this.theme.label.activatedFontColor;
}
private getFontColor(): ResourceColor {
return this.chipNodeInFocus ? this.theme.label.focusFontColor : this.theme.label.fontColor;
}
private getChipNodeBorderColor(): ResourceColor | undefined {
if (this.getBackgroundSystemMaterial()) {
return undefined;
}
let themeChipNode = this.theme.chipNode;
return this.isChipActivated() ? themeChipNode.activatedBorderColor : themeChipNode.borderColor;
}
private getChipNodeBorderWidth(): Dimension | undefined {
if (this.getBackgroundSystemMaterial()) {
return undefined;
}
let themeChipNode = this.theme.chipNode;
return this.isChipActivated() ? themeChipNode.activatedBorderWidth : themeChipNode.defaultBorderWidth;
}
getLabelFontColor(): ResourceColor {
if (this.isChipActivated()) {
return colorMetricsToResourceColor(this.chipV2Options.label.activatedFontColor) ?? this.getActiveFontColor();
}
return colorMetricsToResourceColor(this.chipV2Options.label.fontColor) ?? this.getFontColor();
}
getLabelFontSize(): Dimension {
if (typeof this.chipV2Options.label.fontSize !== 'undefined' &&
this.isValidLength(this.chipV2Options.label.fontSize)) {
return lengthMetricsToDimension(this.chipV2Options.label.fontSize);
}
if (!!this.chipV2Options.fontSize && this.isValidLength(this.chipV2Options.fontSize)) {
return lengthMetricsToDimension(this.chipV2Options.fontSize);
}
if (this.isSmallChipSize()) {
return this.useAdaptiveLineHeight ? this.theme.label.adaptiveItemFontSize : this.theme.label.smallFontSize;
}
return this.useAdaptiveLineHeight ? this.theme.label.adaptiveItemFontSize : this.theme.label.normalFontSize;
}
deleteChip() {
this.getUIContext().animateTo({ curve: Curve.Sharp, duration: 150 }, () => {
this.chipOpacity = 0;
});
this.getUIContext().animateTo({
curve: Curve.FastOutLinearIn,
duration: 150,
onFinish: () => {
this.isChipExist = false;
}
}, () => {
this.chipScale = { x: 0.85, y: 0.85 };
})
}
getChipOnClicked(): Callback<ClickEvent> | undefined {
if (this.onClicked) {
return (event: ClickEvent) => {
this.onClicked!();
}
}
return void 0;
}
private getAccessibilitySelected(): boolean | undefined {
if (this.getChipAccessibilitySelectedType() === ChipV2AccessibilitySelectedType.SELECTED) {
return this.isChipActivated();
}
return void 0;
}
private getAccessibilityChecked(): boolean | undefined {
if (this.getChipAccessibilitySelectedType() === ChipV2AccessibilitySelectedType.CHECKED) {
return this.isChipActivated();
}
return void 0;
}
private getChipAccessibilitySelectedType(): ChipV2AccessibilitySelectedType {
if (typeof this.chipV2Options.activated === 'undefined') {
return ChipV2AccessibilitySelectedType.CLICKED;
}
return this.chipV2Options.accessibilitySelectedType ?? ChipV2AccessibilitySelectedType.CHECKED;
}
private getAccessibilityDescription(): Resource | undefined {
if (typeof this.chipV2Options.accessibilityDescription === 'undefined') {
return void 0;
}
return this.chipV2Options.accessibilityDescription as ESObject as Resource;
}
isChipEnabled(): boolean {
return this.chipV2Options.enabled ?? true;
}
getChipBorderRadius(): Dimension {
if (typeof this.chipV2Options.borderRadius !== 'undefined' && this.isValidLength(this.chipV2Options.borderRadius)) {
return lengthMetricsToDimension(this.chipV2Options.borderRadius);
}
return this.isSmallChipSize() ? this.theme.chipNode.smallBorderRadius : this.theme.chipNode.normalBorderRadius;
}
isSmallChipSize() {
return typeof this.chipV2Options.size === 'string' && this.chipV2Options.size === ChipV2Size.SMALL;
}
getChipBackgroundColor(): ResourceColor {
let themeChipNode = this.theme.chipNode;
if (this.isChipActivated()) {
return this.chipNodeInFocus && !this.isSetActiveChipBgColor() ? themeChipNode.focusActivatedBgColor :
this.getColor(this.chipV2Options.activatedBackgroundColor, themeChipNode.activatedBackgroundColor);
}
return this.chipNodeInFocus && !this.isSetNormalChipBgColor() ? themeChipNode.focusBgColor :
this.getColor(this.chipV2Options.backgroundColor, this.theme.chipNode.backgroundColor);
}
getBackgroundSystemMaterial(): uiMaterial.Material | undefined {
if (deviceInfo.sdkApiVersion < 26) {
return undefined;
}
if (this.isChipActivated()) {
return this.chipV2Options.activatedBackgroundSystemMaterial;
}
return this.chipV2Options.backgroundSystemMaterial;
}
getColor(color: ColorMetrics | undefined, defaultColor: ResourceColor): ResourceColor {
if (!color) {
return defaultColor;
}
try {
return color.color;
} catch (e) {
console.error(`[ChipV2] failed to get color`);
return Color.Transparent;
}
}
isChipActivated() {
return this.chipV2Options.activated ?? false;
}
private getResourceNumber(resource: Resource): number | null {
const resourceManager = this.getUIContext().getHostContext()?.resourceManager;
if (!resourceManager) {
console.error('[ChipV2] failed to get resourceManager');
return null;
}
switch (resource.type) {
case RESOURCE_TYPE_FLOAT:
case RESOURCE_TYPE_INTEGER:
try {
if (resource.id !== -1) {
return resourceManager.getNumber(resource.id);
}
return resourceManager.getNumberByName((resource.params as string[])[0].split('.')[2]);
} catch (error) {
console.error(`[ChipV2] get resource error`);
return null;
}
default:
return null;
}
}
resourceToNumber(resource: Resource, defaultValue: number): number {
if (!resource || !resource.type) {
console.error('[ChipV2] failed: resource get fail');
return defaultValue;
}
const result = this.getResourceNumber(resource);
return result !== null ? result : defaultValue;
}
isValidLength(length: LengthMetrics | undefined): boolean {
if (!length || length.unit === LengthUnit.PERCENT) {
return false;
}
return length.value >= 0;
}
private chipZoomOut() {
if (this.isSuffixIconFocusStyleCustomized) {
this.chipScale = {
x: 1, y: 1
};
}
}
private chipZoomIn() {
if (this.isSuffixIconFocusStyleCustomized) {
this.chipScale = {
x: this.resourceToNumber(this.theme.chipNode.focusBtnScaleX, 1),
y: this.resourceToNumber(this.theme.chipNode.focusBtnScaleY, 1),
};
}
}
private isNeedShowCloseIconMargin(): boolean {
return this.isClosable() && this.isSuffixIconFocusStyleCustomized;
}
private parseLength(length: Length): number | undefined {
if (typeof length === 'number') {
return length;
}
if (typeof length === 'string') {
if (/(\d+)(vp|px|lpx|fp)?/.test(length)) {
const value = parseFloat(RegExp.$1);
const unit = RegExp.$2;
if (unit === 'vp' || unit === '') {
return value;
} else if (unit === 'px') {
return this.getUIContext().px2vp(value);
} else if (unit === 'fp') {
return this.getUIContext().px2vp(this.getUIContext().fp2px(value));
} else if (unit === 'lpx') {
return this.getUIContext().px2vp(this.getUIContext().lpx2px(value));
} else {
return undefined;
}
}
return undefined;
}
if (typeof length === 'object') {
try {
const metrics = LengthMetrics.resource(length as Resource);
if (metrics.unit === LengthUnit.VP) {
return metrics.value;
} else if (metrics.unit === LengthUnit.PX) {
return this.getUIContext().px2vp(metrics.value);
} else if (metrics.unit === LengthUnit.FP) {
return this.getUIContext().px2vp(this.getUIContext().fp2px(metrics.value));
} else {
return this.getUIContext().px2vp(this.getUIContext().lpx2px(metrics.value));
}
} catch (error) {
console.error('Failed to parse length because the type of resource is invalid');
return undefined;
}
}
return undefined;
}
build() {
if (this.isChipExist) {
this.ChipBuilder()
}
}
}
function lengthMetricsToLength(length: LengthMetrics): Length {
if (length.unit === LengthUnit.PX) {
return `${length.value}px`;
} else if (length.unit === LengthUnit.VP) {
return `${length.value}vp`;
} else if (length.unit === LengthUnit.FP) {
return `${length.value}fp`;
} else if (length.unit === LengthUnit.PERCENT) {
return `${length.value}%`;
} else if (length.unit === LengthUnit.LPX) {
return `${length.value}lpx`;
}
return 0;
}
function lengthMetricsToDimension(length: LengthMetrics): Dimension {
if (length.unit === LengthUnit.PX) {
return `${length.value}px`;
} else if (length.unit === LengthUnit.VP) {
return `${length.value}vp`;
} else if (length.unit === LengthUnit.FP) {
return `${length.value}fp`;
} else if (length.unit === LengthUnit.PERCENT) {
return `${length.value}%`;
} else if (length.unit === LengthUnit.LPX) {
return `${length.value}lpx`;
}
return 0;
}
function colorMetricsToResourceColor(color: ColorMetrics | undefined): ResourceColor | undefined {
if (!color) {
return undefined;
}
return color.color;
}