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 { ColorMetrics, LengthMetrics } from '@ohos.arkui.node'; 17import { SymbolGlyphModifier } from '@ohos.arkui.modifier'; 18import { KeyCode } from '@kit.InputKit'; 19import mediaquery from '@ohos.mediaquery'; 20import EnvironmentCallback from '@ohos.app.ability.EnvironmentCallback'; 21import common from '@ohos.app.ability.common'; 22 23export enum ChipSize { 24 NORMAL = 'NORMAL', 25 SMALL = 'SMALL' 26} 27 28enum IconType { 29 PREFIX_ICON = 'PREFIXICON', 30 SUFFIX_ICON = 'SUFFIXICON', 31 PREFIX_SYMBOL = 'PREFIXSYMBOL', 32 SUFFIX_SYMBOL = 'SUFFIXSYMBOL', 33} 34 35enum BreakPointsType { 36 SM = 'SM', 37 MD = 'MD', 38 LG = 'LG' 39} 40 41export enum AccessibilitySelectedType { 42 CLICKED = 0, 43 CHECKED = 1, 44 SELECTED = 2, 45} 46 47export interface IconCommonOptions { 48 src: ResourceStr; 49 size?: SizeOptions; 50 fillColor?: ResourceColor; 51 activatedFillColor?: ResourceColor; 52} 53 54export interface SuffixIconOptions extends IconCommonOptions { 55 action?: () => void; 56 accessibilityText?: ResourceStr; 57 accessibilityDescription?: ResourceStr; 58 accessibilityLevel?: string; 59} 60 61export interface PrefixIconOptions extends IconCommonOptions {} 62 63export interface AccessibilityOptions { 64 accessibilityLevel?: string; 65 accessibilityText?: ResourceStr; 66 accessibilityDescription?: ResourceStr; 67} 68 69export interface CloseOptions extends AccessibilityOptions {} 70 71export interface ChipSymbolGlyphOptions { 72 normal?: SymbolGlyphModifier; 73 activated?: SymbolGlyphModifier; 74} 75 76export interface ChipSuffixSymbolGlyphOptions { 77 normalAccessibility?: AccessibilityOptions; 78 activatedAccessibility?: AccessibilityOptions; 79 action?: VoidCallback; 80} 81 82export interface LabelMarginOptions { 83 left?: Dimension; 84 right?: Dimension; 85} 86 87export interface LocalizedLabelMarginOptions { 88 start?: LengthMetrics; 89 end?: LengthMetrics; 90} 91 92export interface LabelOptions { 93 text: string; 94 fontSize?: Dimension; 95 fontColor?: ResourceColor; 96 activatedFontColor?: ResourceColor; 97 fontFamily?: string; 98 labelMargin?: LabelMarginOptions; 99 localizedLabelMargin?: LocalizedLabelMarginOptions; 100} 101 102interface IconTheme { 103 normalSize: SizeOptions; 104 smallSize: SizeOptions; 105 fillColor: ResourceColor; 106 activatedFillColor: ResourceColor; 107 focusFillColor: ResourceColor; 108 focusActivatedColor: ResourceColor; 109} 110 111interface PrefixIconTheme extends IconTheme {} 112 113interface SuffixIconTheme extends IconTheme { 114 defaultDeleteIcon: ResourceStr; 115 focusable: boolean; 116 isShowMargin: Resource; 117} 118 119interface DefaultSymbolTheme { 120 normalFontColor: Array<ResourceColor>; 121 activatedFontColor: Array<ResourceColor>; 122 smallSymbolFontSize: Length; 123 normalSymbolFontSize: Length; 124 defaultEffect: number; 125} 126 127interface LabelTheme { 128 normalFontSize: Dimension; 129 smallFontSize: Dimension; 130 focusFontColor: ResourceColor; 131 focusActiveFontColor: ResourceColor; 132 fontColor: ResourceColor; 133 activatedFontColor: ResourceColor; 134 fontFamily: string; 135 normalMargin: Margin; 136 localizedNormalMargin: LocalizedMargin; 137 smallMargin: Margin; 138 localizedSmallMargin: LocalizedMargin; 139 defaultFontSize: Dimension; 140 fontWeight: Resource; 141} 142 143interface ChipNodeOpacity { 144 normal: number; 145 hover: number; 146 pressed: number; 147} 148 149interface ChipNodeConstraintWidth { 150 breakPointMinWidth: number, 151 breakPointSmMaxWidth: number, 152 breakPointMdMaxWidth: number, 153 breakPointLgMaxWidth: number, 154} 155 156interface ChipNodeTheme { 157 suitAgeScale: number; 158 minLabelWidth: Dimension; 159 normalHeight: Dimension; 160 smallHeight: Dimension; 161 enabled: boolean; 162 activated: boolean; 163 backgroundColor: ResourceColor; 164 activatedBackgroundColor: ResourceColor; 165 focusOutlineColor: ResourceColor; 166 borderColor: ResourceColor, 167 defaultBorderWidth: Dimension; 168 activatedBorderColor: ResourceColor; 169 focusBtnScaleX: Resource; 170 focusBtnScaleY: Resource; 171 focusBgColor: ResourceColor; 172 focusActivatedBgColor: ResourceColor; 173 normalShadowStyle: Resource; 174 smallShadowStyle: Resource; 175 focusOutlineMargin: number; 176 normalBorderRadius: Dimension; 177 smallBorderRadius: Dimension; 178 borderWidth: number; 179 localizedNormalPadding: LocalizedPadding; 180 localizedSmallPadding: LocalizedPadding; 181 hoverBlendColor: ResourceColor; 182 pressedBlendColor: ResourceColor; 183 opacity: ChipNodeOpacity; 184 breakPointConstraintWidth: ChipNodeConstraintWidth; 185} 186 187interface ChipTheme { 188 prefixIcon: PrefixIconTheme; 189 label: LabelTheme; 190 suffixIcon: SuffixIconTheme; 191 defaultSymbol: DefaultSymbolTheme; 192 chipNode: ChipNodeTheme; 193} 194 195const RESOURCE_TYPE_STRING = 10003; 196const RESOURCE_TYPE_FLOAT = 10002; 197const RESOURCE_TYPE_INTEGER = 10007; 198 199interface ChipOptions { 200 prefixIcon?: PrefixIconOptions; 201 prefixSymbol?: ChipSymbolGlyphOptions; 202 label: LabelOptions; 203 suffixIcon?: SuffixIconOptions; 204 suffixSymbol?: ChipSymbolGlyphOptions; 205 suffixSymbolOptions?: ChipSuffixSymbolGlyphOptions; 206 allowClose?: boolean; 207 closeOptions?: CloseOptions; 208 enabled?: boolean; 209 activated?: boolean; 210 backgroundColor?: ResourceColor; 211 activatedBackgroundColor?: ResourceColor; 212 borderRadius?: Dimension; 213 size?: ChipSize | SizeOptions; 214 direction?: Direction; 215 accessibilitySelectedType?: AccessibilitySelectedType; 216 accessibilityDescription?: ResourceStr; 217 accessibilityLevel?: string; 218 onClose?: () => void 219 onClicked?: () => void 220} 221 222@Builder 223export function Chip(options: ChipOptions) { 224 ChipComponent({ 225 chipSize: options.size, 226 prefixIcon: options.prefixIcon, 227 prefixSymbol: options.prefixSymbol, 228 label: options.label, 229 suffixIcon: options.suffixIcon, 230 suffixSymbol: options.suffixSymbol, 231 suffixSymbolOptions: options.suffixSymbolOptions, 232 allowClose: options.allowClose, 233 closeOptions: options.closeOptions, 234 chipEnabled: options.enabled, 235 chipActivated: options.activated, 236 chipNodeBackgroundColor: options.backgroundColor, 237 chipNodeActivatedBackgroundColor: options.activatedBackgroundColor, 238 chipNodeRadius: options.borderRadius, 239 chipDirection: options.direction, 240 chipAccessibilitySelectedType: options.accessibilitySelectedType, 241 chipAccessibilityDescription: options.accessibilityDescription, 242 chipAccessibilityLevel: options.accessibilityLevel, 243 onClose: options.onClose, 244 onClicked: options.onClicked, 245 }) 246} 247 248 249@Component 250export struct ChipComponent { 251 private theme: ChipTheme = { 252 prefixIcon: { 253 normalSize: { 254 width: $r('sys.float.chip_normal_icon_size'), 255 height: $r('sys.float.chip_normal_icon_size') 256 }, 257 smallSize: { 258 width: $r('sys.float.chip_small_icon_size'), 259 height: $r('sys.float.chip_small_icon_size') 260 }, 261 fillColor: $r('sys.color.chip_usually_icon_color'), 262 activatedFillColor: $r('sys.color.chip_active_icon_color'), 263 focusFillColor: $r('sys.color.chip_icon_focus_fill'), 264 focusActivatedColor: $r('sys.color.chip_icon_activated_focus_color'), 265 }, 266 label: { 267 normalFontSize: $r('sys.float.chip_normal_font_size'), 268 smallFontSize: $r('sys.float.chip_small_font_size'), 269 focusFontColor: $r('sys.color.chip_focus_text'), 270 focusActiveFontColor: $r('sys.color.chip_activated_focus_font_color'), 271 fontColor: $r('sys.color.chip_font_color'), 272 activatedFontColor: $r('sys.color.chip_activated_fontcolor'), 273 fontFamily: 'HarmonyOS Sans', 274 fontWeight: $r('sys.float.chip_text_font_weight'), 275 normalMargin: { 276 left: 6, 277 right: 6, 278 top: 0, 279 bottom: 0 280 }, 281 smallMargin: { 282 left: 4, 283 right: 4, 284 top: 0, 285 bottom: 0 286 }, 287 defaultFontSize: 14, 288 localizedNormalMargin: { 289 start: LengthMetrics.resource($r('sys.float.chip_normal_text_margin')), 290 end: LengthMetrics.resource($r('sys.float.chip_normal_text_margin')), 291 top: LengthMetrics.vp(0), 292 bottom: LengthMetrics.vp(0) 293 }, 294 localizedSmallMargin: { 295 start: LengthMetrics.resource($r('sys.float.chip_small_text_margin')), 296 end: LengthMetrics.resource($r('sys.float.chip_small_text_margin')), 297 top: LengthMetrics.vp(0), 298 bottom: LengthMetrics.vp(0), 299 } 300 }, 301 suffixIcon: { 302 normalSize: { 303 width: $r('sys.float.chip_normal_icon_size'), 304 height: $r('sys.float.chip_normal_icon_size') 305 }, 306 smallSize: { 307 width: $r('sys.float.chip_small_icon_size'), 308 height: $r('sys.float.chip_small_icon_size') 309 }, 310 fillColor: $r('sys.color.chip_usually_icon_color'), 311 activatedFillColor: $r('sys.color.chip_active_icon_color'), 312 focusFillColor: $r('sys.color.chip_icon_focus_fill'), 313 focusActivatedColor: $r('sys.color.chip_icon_activated_focus_color'), 314 defaultDeleteIcon: $r('sys.media.ohos_ic_public_cancel', 16, 16), 315 focusable: false, 316 isShowMargin: $r('sys.float.chip_show_close_icon_margin'), 317 }, 318 defaultSymbol: { 319 normalFontColor: [$r('sys.color.chip_usually_icon_color')], 320 activatedFontColor: [$r('sys.color.chip_active_icon_color')], 321 normalSymbolFontSize: LengthMetrics.resource($r('sys.float.chip_normal_icon_size')).value as Length, 322 smallSymbolFontSize: LengthMetrics.resource($r('sys.float.chip_small_icon_size')).value as Length, 323 defaultEffect: -1, 324 }, 325 chipNode: { 326 suitAgeScale: 1.75, 327 minLabelWidth: 12, 328 normalHeight: $r('sys.float.chip_normal_height'), 329 smallHeight: $r('sys.float.chip_small_height'), 330 enabled: true, 331 activated: false, 332 backgroundColor: $r('sys.color.chip_background_color'), 333 activatedBackgroundColor: $r('sys.color.chip_container_activated_color'), 334 focusOutlineColor: $r('sys.color.ohos_id_color_focused_outline'), 335 focusOutlineMargin: 2, 336 borderColor: $r('sys.color.chip_border_color'), 337 defaultBorderWidth: $r('sys.float.chip_border_width'), 338 activatedBorderColor: $r('sys.color.chip_activated_border_color'), 339 normalBorderRadius: $r('sys.float.chip_border_radius_normal'), 340 smallBorderRadius: $r('sys.float.chip_border_radius_small'), 341 borderWidth: 2, 342 focusBtnScaleX: $r('sys.float.chip_focused_btn_scale'), 343 focusBtnScaleY: $r('sys.float.chip_focused_btn_scale'), 344 localizedNormalPadding: { 345 start: LengthMetrics.resource($r('sys.float.chip_normal_text_padding')), 346 end: LengthMetrics.resource($r('sys.float.chip_normal_text_padding')), 347 top: LengthMetrics.vp(4), 348 bottom: LengthMetrics.vp(4) 349 }, 350 localizedSmallPadding: { 351 start: LengthMetrics.resource($r('sys.float.chip_small_text_padding')), 352 end: LengthMetrics.resource($r('sys.float.chip_small_text_padding')), 353 top: LengthMetrics.vp(4), 354 bottom: LengthMetrics.vp(4) 355 }, 356 hoverBlendColor: $r('sys.color.chip_hover_color'), 357 pressedBlendColor: $r('sys.color.chip_press_color'), 358 focusBgColor: $r('sys.color.chip_focus_color'), 359 focusActivatedBgColor: $r('sys.color.chip_container_activated_focus_color'), 360 opacity: { normal: 1, hover: 0.95, pressed: 0.9 }, 361 normalShadowStyle: $r('sys.float.chip_normal_shadow_style'), 362 smallShadowStyle: $r('sys.float.chip_small_shadow_style'), 363 breakPointConstraintWidth: { 364 breakPointMinWidth: 128, 365 breakPointSmMaxWidth: 156, 366 breakPointMdMaxWidth: 280, 367 breakPointLgMaxWidth: 400 368 } 369 } 370 }; 371 @Prop chipSize?: ChipSize | SizeOptions = ChipSize.NORMAL; 372 @Prop allowClose?: boolean; 373 @Prop closeOptions?: CloseOptions; 374 @Prop chipDirection?: Direction = Direction.Auto; 375 @Prop prefixIcon?: PrefixIconOptions; 376 @Prop prefixSymbol?: ChipSymbolGlyphOptions; 377 @Require @Prop label: LabelOptions; 378 @Prop suffixIcon?: SuffixIconOptions; 379 @Prop suffixSymbol?: ChipSymbolGlyphOptions; 380 @Prop suffixSymbolOptions?: ChipSuffixSymbolGlyphOptions; 381 @Prop chipNodeBackgroundColor?: ResourceColor; 382 @Prop chipNodeActivatedBackgroundColor?: ResourceColor; 383 @Prop chipNodeRadius?: Dimension; 384 @Prop chipEnabled?: boolean = true; 385 @Prop chipActivated?: boolean; 386 @Prop chipAccessibilitySelectedType?: AccessibilitySelectedType; 387 @Prop chipAccessibilityDescription?: ResourceStr; 388 @Prop chipAccessibilityLevel?: string; 389 @State isChipExist: boolean = true; 390 @State chipScale: ScaleOptions = { x: 1, y: 1 }; 391 @State chipOpacity: number = 1; 392 @State suffixSymbolHeight: number = 0; 393 @State suffixSymbolWidth: number = 0; 394 @State breakPoint: BreakPointsType = BreakPointsType.SM; 395 @State fontSizeScale: number = 1; 396 private isSuffixIconFocusStyleCustomized = this.resourceToNumber(this.theme.suffixIcon.isShowMargin, 0) !== 0; 397 private isSuffixIconFocusable = this.resourceToNumber(this.theme.suffixIcon.isShowMargin, 0) !== 0; 398 public onClose?: VoidCallback; 399 public onClicked?: VoidCallback; 400 @State chipNodeInFocus: boolean = false; 401 private smListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(0vp<width) and (width<600vp)'); 402 private mdListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(600vp<=width) and (width<840vp)'); 403 private lgListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(840vp<=width)'); 404 private symbolEffect: SymbolEffect = new SymbolEffect(); 405 private environmentCallbackID?: number = undefined; 406 private environmentCallback: EnvironmentCallback = { 407 onConfigurationUpdated: (configuration) => { 408 this.fontSizeScale = configuration.fontSizeScale ?? 1; 409 }, 410 onMemoryLevel() { 411 } 412 }; 413 414 aboutToAppear(): void { 415 this.smListener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => { 416 if (mediaQueryResult.matches) { 417 this.breakPoint = BreakPointsType.SM; 418 } 419 }); 420 this.mdListener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => { 421 if (mediaQueryResult.matches) { 422 this.breakPoint = BreakPointsType.MD; 423 } 424 }); 425 this.lgListener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => { 426 if (mediaQueryResult.matches) { 427 this.breakPoint = BreakPointsType.LG; 428 } 429 }); 430 let abilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext | undefined; 431 if (abilityContext) { 432 this.fontSizeScale = abilityContext.config?.fontSizeScale ?? 1; 433 this.environmentCallbackID = abilityContext.getApplicationContext().on('environment', this.environmentCallback); 434 } 435 } 436 437 aboutToDisappear(): void { 438 this.smListener.off('change'); 439 this.mdListener.off('change'); 440 this.lgListener.off('change'); 441 if (this.environmentCallbackID) { 442 this.getUIContext()?.getHostContext()?.getApplicationContext().off('environment', this.environmentCallbackID); 443 this.environmentCallbackID = void 0; 444 } 445 } 446 447 private isSetActiveChipBgColor(): boolean { 448 if (this.chipNodeActivatedBackgroundColor) { 449 return false; 450 } 451 try { 452 return ColorMetrics.resourceColor(this.chipNodeActivatedBackgroundColor).color !== 453 ColorMetrics.resourceColor(this.theme.chipNode.activatedBackgroundColor).color; 454 } catch (error) { 455 console.error(`[Chip] failed to get ColorMetrics.resourceColor`); 456 return false; 457 } 458 } 459 460 private isSetNormalChipBgColor(): boolean { 461 if (this.chipNodeBackgroundColor) { 462 return false; 463 } 464 try { 465 return ColorMetrics.resourceColor(this.chipNodeBackgroundColor).color !== 466 ColorMetrics.resourceColor(this.theme.chipNode.backgroundColor).color; 467 } catch (error) { 468 console.error(`[Chip] failed to get resourceColor`); 469 return false; 470 } 471 } 472 473 private getShadowStyles(): ShadowStyle | undefined { 474 if (!this.chipNodeInFocus) { 475 return undefined; 476 } 477 return this.resourceToNumber(this.isSmallChipSize() ? this.theme.chipNode.smallShadowStyle : 478 this.theme.chipNode.normalShadowStyle, -1); 479 } 480 481 @Builder 482 ChipBuilder() { 483 Button({ type: ButtonType.Normal }) { 484 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 485 if (this.hasPrefixSymbol()) { 486 SymbolGlyph() 487 .fontSize(this.defaultSymbolFontsize()) 488 .fontColor(this.getDefaultSymbolColor(IconType.PREFIX_SYMBOL)) 489 .flexShrink(0) 490 .attributeModifier(this.getPrefixSymbolModifier()) 491 .effectStrategy(SymbolEffectStrategy.NONE) 492 .symbolEffect(this.symbolEffect, false) 493 .symbolEffect(this.symbolEffect, this.theme.defaultSymbol.defaultEffect) 494 } else if (this.prefixIcon?.src) { 495 Image(this.prefixIcon.src) 496 .direction(this.chipDirection) 497 .size(this.getPrefixIconSize()) 498 .fillColor(this.getPrefixIconFilledColor()) 499 .objectFit(ImageFit.Cover) 500 .focusable(false) 501 .flexShrink(0) 502 .draggable(false) 503 } 504 505 Text(this.getChipText()) 506 .draggable(false) 507 .flexShrink(1) 508 .focusable(true) 509 .maxLines(1) 510 .textOverflow({ overflow: TextOverflow.Ellipsis }) 511 .textAlign(TextAlign.Center) 512 .direction(this.chipDirection) 513 .fontSize(this.getLabelFontSize()) 514 .fontColor(this.getLabelFontColor()) 515 .fontFamily(this.getLabelFontFamily()) 516 .fontWeight(this.getLabelFontWeight()) 517 .margin(this.getLabelMargin()) 518 519 if (this.hasSuffixSymbol()) { 520 Button({ type: ButtonType.Normal }) { 521 SymbolGlyph() 522 .fontSize(this.defaultSymbolFontsize()) 523 .fontColor(this.getDefaultSymbolColor(IconType.SUFFIX_SYMBOL)) 524 .attributeModifier(this.getSuffixSymbolModifier()) 525 .effectStrategy(SymbolEffectStrategy.NONE) 526 .symbolEffect(this.symbolEffect, false) 527 .symbolEffect(this.symbolEffect, this.theme.defaultSymbol.defaultEffect) 528 } 529 .onClick(this.getSuffixSymbolAction()) 530 .accessibilityText(this.getSuffixSymbolAccessibilityText()) 531 .accessibilityDescription(this.getSuffixSymbolAccessibilityDescription()) 532 .accessibilityLevel(this.getSuffixSymbolAccessibilityLevel()) 533 .flexShrink(0) 534 .backgroundColor(Color.Transparent) 535 .borderRadius(0) 536 .padding(0) 537 .stateEffect(false) 538 .hoverEffect(HoverEffect.None) 539 .focusable(this.isSuffixIconFocusable) 540 } else if (this.suffixIcon?.src) { 541 Button({ type: ButtonType.Normal }) { 542 Image(this.suffixIcon.src) 543 .direction(this.chipDirection) 544 .size(this.getSuffixIconSize()) 545 .fillColor(this.getSuffixIconFilledColor()) 546 .objectFit(ImageFit.Cover) 547 .draggable(false) 548 } 549 .backgroundColor(Color.Transparent) 550 .borderRadius(0) 551 .padding(0) 552 .flexShrink(0) 553 .stateEffect(false) 554 .hoverEffect(HoverEffect.None) 555 .size(this.getSuffixIconSize()) 556 .accessibilityText(this.getSuffixIconAccessibilityText()) 557 .accessibilityDescription(this.getSuffixIconAccessibilityDescription()) 558 .accessibilityLevel(this.getSuffixIconAccessibilityLevel()) 559 .onClick(this.getSuffixIconAction()) 560 .focusable(this.isSuffixIconFocusable) 561 } else if (this.isClosable()) { 562 Button({ type: ButtonType.Normal }) { 563 SymbolGlyph($r('sys.symbol.xmark')) 564 .fontSize(this.defaultSymbolFontsize()) 565 .fontColor(this.getDefaultSymbolColor(IconType.SUFFIX_SYMBOL)) 566 } 567 .backgroundColor(Color.Transparent) 568 .borderRadius(0) 569 .padding(0) 570 .flexShrink(0) 571 .stateEffect(false) 572 .hoverEffect(HoverEffect.None) 573 .accessibilityText(this.getCloseIconAccessibilityText()) 574 .accessibilityDescription(this.getCloseIconAccessibilityDescription()) 575 .accessibilityLevel(this.getCloseIconAccessibilityLevel()) 576 .onClick(() => { 577 if (!this.isChipEnabled()) { 578 return; 579 } 580 this.onClose?.(); 581 this.deleteChip(); 582 }) 583 .focusable(this.isSuffixIconFocusable) 584 } 585 } 586 .direction(this.chipDirection) 587 .padding(this.getChipPadding()) 588 .size(this.getChipSize()) 589 .constraintSize(this.getChipConstraintSize()) 590 } 591 .clip(false) 592 .shadow(this.getShadowStyles()) 593 .padding(0) 594 .focusable(true) 595 .size(this.getChipSize()) 596 .enabled(this.isChipEnabled()) 597 .direction(this.chipDirection) 598 .backgroundColor(this.getChipBackgroundColor()) 599 .borderWidth(this.theme.chipNode.defaultBorderWidth) 600 .borderColor(this.getChipNodeBorderColor()) 601 .borderRadius(this.getChipBorderRadius()) 602 .scale(this.chipScale) 603 .opacity(this.chipOpacity) 604 .accessibilityGroup(true) 605 .accessibilityDescription(this.getAccessibilityDescription()) 606 .accessibilityLevel(this.getAccessibilityLevel()) 607 .accessibilityChecked(this.getAccessibilityChecked()) 608 .accessibilitySelected(this.getAccessibilitySelected()) 609 .onClick(this.getChipOnClicked()) 610 .onKeyEvent((event) => { 611 if (!event || event.type === null || event.type !== KeyType.Down) { 612 return; 613 } 614 let isDeleteChip = event.keyCode === KeyCode.KEYCODE_FORWARD_DEL; 615 let isEnterDeleteChip = event.keyCode === KeyCode.KEYCODE_ENTER && this.allowClose !== false && 616 !this.suffixIcon?.src && this.isSuffixIconFocusStyleCustomized; 617 if (isDeleteChip || isEnterDeleteChip) { 618 this.deleteChip(); 619 } 620 }) 621 .onFocus(() => { 622 if (this.isSuffixIconFocusStyleCustomized) { 623 this.chipNodeInFocus = true; 624 } 625 this.chipZoomIn(); 626 }) 627 .onBlur(() => { 628 if (this.isSuffixIconFocusStyleCustomized) { 629 this.chipNodeInFocus = false; 630 } 631 this.chipZoomOut(); 632 }) 633 .hoverStyle() 634 } 635 636 @Styles 637 hoverStyle() { 638 .onHover(!this.isSuffixIconFocusStyleCustomized ? undefined : (isHover: boolean) => { 639 isHover ? this.chipZoomIn() : this.chipZoomOut(); 640 }) 641 } 642 643 private getCloseIconAccessibilityLevel(): string { 644 if (this.closeOptions?.accessibilityLevel === 'no' || this.closeOptions?.accessibilityLevel === 'no-hide-descendants') { 645 return this.closeOptions.accessibilityLevel; 646 } 647 return 'yes'; 648 } 649 650 private getCloseIconAccessibilityDescription(): Resource | undefined { 651 if (typeof this.closeOptions?.accessibilityDescription === 'undefined') { 652 return void 0; 653 } 654 return this.closeOptions.accessibilityDescription as Resource; 655 } 656 657 private getCloseIconAccessibilityText(): Resource { 658 if (typeof this.closeOptions?.accessibilityText === 'undefined') { 659 return $r('sys.string.delete_used_for_accessibility_text'); 660 } 661 return this.closeOptions.accessibilityText as ESObject as Resource; 662 } 663 664 getSuffixIconAction(): Callback<ClickEvent> | undefined { 665 if (this.suffixIcon?.src) { 666 if (!this.suffixIcon?.action) { 667 return void 0; 668 } 669 return () => { 670 if (this.isChipEnabled()) { 671 this.suffixIcon?.action?.(); 672 } 673 }; 674 } 675 return void 0; 676 } 677 678 getSuffixIconFilledColor(): ResourceColor { 679 if (this.isChipActivated()) { 680 return this.suffixIcon?.activatedFillColor ?? this.getDefaultActiveIconColor(IconType.PREFIX_ICON); 681 } 682 return this.suffixIcon?.fillColor ?? this.getDefaultFillIconColor(IconType.SUFFIX_ICON); 683 } 684 685 getSuffixIconSize(): SizeOptions { 686 let suffixIconSize: SizeOptions = { width: 0, height: 0 }; 687 if (typeof this.suffixIcon?.size?.width !== 'undefined' && this.isValidLength(this.suffixIcon.size.width)) { 688 suffixIconSize.width = this.suffixIcon.size.width; 689 } else { 690 suffixIconSize.width = 691 this.isSmallChipSize() ? this.theme.suffixIcon.smallSize.width : this.theme.suffixIcon.normalSize.width; 692 } 693 if (typeof this.suffixIcon?.size?.height !== 'undefined' && this.isValidLength(this.suffixIcon.size.height)) { 694 suffixIconSize.height = this.suffixIcon.size.height; 695 } else { 696 suffixIconSize.height = 697 this.isSmallChipSize() ? this.theme.suffixIcon.smallSize.height : this.theme.suffixIcon.normalSize.height; 698 } 699 return suffixIconSize; 700 } 701 702 getSuffixIconAccessibilityLevel(): string { 703 if (this.suffixIcon?.accessibilityLevel === 'no' || this.suffixIcon?.accessibilityLevel === 'no-hide-descendants') { 704 return this.suffixIcon.accessibilityLevel; 705 } 706 return this.suffixIcon?.action ? 'yes' : 'no'; 707 } 708 709 getSuffixIconAccessibilityDescription(): Resource | undefined { 710 if (typeof this.suffixIcon?.accessibilityDescription === 'undefined') { 711 return void 0; 712 } 713 return this.suffixIcon.accessibilityDescription as ESObject as Resource; 714 } 715 716 getSuffixIconAccessibilityText(): Resource | undefined { 717 if (typeof this.suffixIcon?.accessibilityText === 'undefined') { 718 return void 0; 719 } 720 721 return this.suffixIcon.accessibilityText as ESObject as Resource; 722 } 723 724 isClosable(): boolean { 725 return this.allowClose ?? true; 726 } 727 728 getSuffixSymbolModifier(): SymbolGlyphModifier | undefined { 729 if (this.isChipActivated()) { 730 return this.suffixSymbol?.activated; 731 } 732 return this.suffixSymbol?.normal; 733 } 734 735 getSuffixSymbolAccessibilityLevel(): string { 736 if (this.isChipActivated()) { 737 if (this.suffixSymbolOptions?.activatedAccessibility?.accessibilityLevel === 'no' || 738 this.suffixSymbolOptions?.activatedAccessibility?.accessibilityLevel === 'no-hide-descendants') { 739 return this.suffixSymbolOptions.activatedAccessibility.accessibilityLevel; 740 } 741 return this.suffixSymbolOptions?.action ? 'yes' : 'no'; 742 } 743 if (this.suffixSymbolOptions?.normalAccessibility?.accessibilityLevel === 'no' || 744 this.suffixSymbolOptions?.normalAccessibility?.accessibilityLevel === 'no-hide-descendants') { 745 return this.suffixSymbolOptions.normalAccessibility.accessibilityLevel; 746 } 747 return this.suffixSymbolOptions?.action ? 'yes' : 'no'; 748 } 749 750 getSuffixSymbolAccessibilityDescription(): Resource | undefined { 751 if (this.isChipActivated()) { 752 if (typeof this.suffixSymbolOptions?.activatedAccessibility?.accessibilityDescription !== 'undefined') { 753 return this.suffixSymbolOptions.activatedAccessibility.accessibilityDescription as Resource; 754 } 755 return void 0; 756 } 757 if (typeof this.suffixSymbolOptions?.normalAccessibility?.accessibilityDescription !== 'undefined') { 758 return this.suffixSymbolOptions.normalAccessibility.accessibilityDescription as Resource; 759 } 760 return void 0; 761 } 762 763 getSuffixSymbolAccessibilityText(): Resource | undefined { 764 if (this.isChipActivated()) { 765 if (typeof this.suffixSymbolOptions?.activatedAccessibility?.accessibilityText !== 'undefined') { 766 return this.suffixSymbolOptions.activatedAccessibility.accessibilityText as Resource; 767 } 768 return void 0; 769 } 770 if (typeof this.suffixSymbolOptions?.normalAccessibility?.accessibilityText !== 'undefined') { 771 return this.suffixSymbolOptions.normalAccessibility.accessibilityText as Resource; 772 } 773 return void 0; 774 } 775 776 getSuffixSymbolAction(): Callback<ClickEvent> | undefined { 777 if (typeof this.suffixSymbolOptions?.action === 'undefined') { 778 return void 0; 779 } 780 return () => { 781 if (!this.isChipEnabled()) { 782 return; 783 } 784 this.suffixSymbolOptions?.action?.(); 785 }; 786 } 787 788 hasSuffixSymbol(): boolean { 789 return !!(this.suffixSymbol?.normal || this.suffixSymbol?.activated); 790 } 791 792 getPrefixIconFilledColor(): ResourceColor { 793 if (this.isChipActivated()) { 794 return this.prefixIcon?.activatedFillColor ?? this.getDefaultActiveIconColor(IconType.PREFIX_ICON); 795 } 796 return this.prefixIcon?.fillColor ?? this.getDefaultFillIconColor(IconType.PREFIX_ICON); 797 } 798 799 getPrefixIconSize(): SizeOptions { 800 let prefixIconSize: SizeOptions = { width: 0, height: 0 }; 801 if (typeof this.prefixIcon?.size?.width !== 'undefined' && this.isValidLength(this.prefixIcon.size.width)) { 802 prefixIconSize.width = this.prefixIcon.size.width; 803 } else { 804 prefixIconSize.width = 805 this.isSmallChipSize() ? this.theme.prefixIcon.smallSize.width : this.theme.prefixIcon.normalSize.width; 806 } 807 if (typeof this.prefixIcon?.size?.height !== 'undefined' && this.isValidLength(this.prefixIcon.size.height)) { 808 prefixIconSize.height = this.prefixIcon.size.height; 809 } else { 810 prefixIconSize.height = 811 this.isSmallChipSize() ? this.theme.prefixIcon.smallSize.height : this.theme.prefixIcon.normalSize.height; 812 } 813 return prefixIconSize; 814 } 815 816 getPrefixSymbolModifier(): SymbolGlyphModifier | undefined { 817 if (this.isChipActivated()) { 818 return this.prefixSymbol?.activated; 819 } 820 return this.prefixSymbol?.normal; 821 } 822 823 getDefaultSymbolColor(iconType: string): ResourceColor[] { 824 return this.isChipActivated() ? this.getSymbolActiveColor(iconType) : 825 this.getSymbolFillColor(iconType); 826 } 827 828 private getDefaultActiveIconColor(iconType: string): ResourceColor { 829 if (iconType === IconType.PREFIX_ICON) { 830 return this.chipNodeInFocus ? this.theme.prefixIcon.focusActivatedColor : 831 this.theme.prefixIcon.activatedFillColor; 832 } else { 833 return this.chipNodeInFocus ? this.theme.suffixIcon.focusActivatedColor : 834 this.theme.suffixIcon.activatedFillColor; 835 } 836 } 837 838 private getDefaultFillIconColor(iconType: string): ResourceColor { 839 if (iconType === IconType.PREFIX_ICON) { 840 return this.chipNodeInFocus ? this.theme.prefixIcon.focusFillColor : this.theme.prefixIcon.fillColor; 841 } else { 842 return this.chipNodeInFocus ? this.theme.suffixIcon.focusFillColor : this.theme.suffixIcon.fillColor; 843 } 844 } 845 846 private getSymbolActiveColor(iconType: string): ResourceColor[] { 847 if (!this.chipNodeInFocus) { 848 return this.theme.defaultSymbol.activatedFontColor; 849 } 850 if (iconType === IconType.PREFIX_SYMBOL) { 851 return [this.theme.prefixIcon.focusActivatedColor]; 852 } 853 if (iconType === IconType.SUFFIX_SYMBOL) { 854 return [this.theme.suffixIcon.focusActivatedColor]; 855 } 856 return this.theme.defaultSymbol.activatedFontColor; 857 } 858 859 private getSymbolFillColor(iconType?: string): ResourceColor[] { 860 if (!this.chipNodeInFocus) { 861 return this.theme.defaultSymbol.normalFontColor; 862 } 863 if (iconType === IconType.PREFIX_SYMBOL) { 864 return [this.theme.prefixIcon.focusFillColor]; 865 } 866 if (iconType === IconType.SUFFIX_SYMBOL) { 867 return [this.theme.suffixIcon.focusFillColor]; 868 } 869 return this.theme.defaultSymbol.normalFontColor; 870 } 871 872 hasPrefixSymbol(): boolean { 873 return !!(this.prefixSymbol?.normal || this.prefixSymbol?.activated); 874 } 875 876 getChipConstraintSize(): ConstraintSizeOptions | undefined { 877 const constraintSize: ConstraintSizeOptions = {}; 878 if (typeof this.chipSize === 'string') { 879 constraintSize.maxWidth = this.getChipMaxWidth(); 880 constraintSize.minHeight = 881 this.chipSize === ChipSize.SMALL ? this.theme.chipNode.smallHeight : this.theme.chipNode.normalHeight; 882 } else { 883 if (typeof this.chipSize?.width === 'undefined' || !this.isValidLength(this.chipSize.width)) { 884 constraintSize.maxWidth = this.getChipMaxWidth(); 885 } 886 if (typeof this.chipSize?.height === 'undefined' || !this.isValidLength(this.chipSize.height)) { 887 constraintSize.minHeight = this.theme.chipNode.normalHeight; 888 } 889 } 890 return constraintSize; 891 } 892 893 getChipMaxWidth(): Length | undefined { 894 if (this.fontSizeScale >= this.theme.chipNode.suitAgeScale) { 895 return void 0; 896 } 897 if (this.breakPoint === BreakPointsType.SM) { 898 return this.theme.chipNode.breakPointConstraintWidth.breakPointSmMaxWidth; 899 } 900 if (this.breakPoint === BreakPointsType.MD) { 901 return this.theme.chipNode.breakPointConstraintWidth.breakPointMdMaxWidth; 902 } 903 if (this.breakPoint === BreakPointsType.LG) { 904 return this.theme.chipNode.breakPointConstraintWidth.breakPointLgMaxWidth; 905 } 906 return void 0; 907 } 908 909 getChipSize(): SizeOptions | undefined { 910 const chipSize: SizeOptions = { 911 width: 'auto', 912 height: 'auto' 913 }; 914 915 if (typeof this.chipSize !== 'string') { 916 if (typeof this.chipSize?.width !== 'undefined' && this.isValidLength(this.chipSize.width)) { 917 chipSize.width = this.chipSize.width; 918 } 919 if (typeof this.chipSize?.height !== 'undefined' && this.isValidLength(this.chipSize.height)) { 920 chipSize.height = this.chipSize.height; 921 } 922 } 923 924 return chipSize; 925 } 926 927 getChipPadding(): Length | Padding | LocalizedPadding { 928 return this.isSmallChipSize() ? this.theme.chipNode.localizedSmallPadding : 929 this.theme.chipNode.localizedNormalPadding; 930 } 931 932 getLabelMargin(): Length | Padding | LocalizedPadding { 933 const localizedLabelMargin: LocalizedMargin = { 934 start: LengthMetrics.vp(0), 935 end: LengthMetrics.vp(0), 936 }; 937 const defaultLocalizedMargin = 938 this.isSmallChipSize() ? this.theme.label.localizedSmallMargin : this.theme.label.localizedNormalMargin; 939 940 if (typeof this.label?.localizedLabelMargin?.start !== 'undefined' && 941 this.label.localizedLabelMargin.start.value >= 0) { 942 localizedLabelMargin.start = this.label.localizedLabelMargin.start; 943 } else if (this.hasPrefix()) { 944 localizedLabelMargin.start = defaultLocalizedMargin.start; 945 } 946 947 if (typeof this.label?.localizedLabelMargin?.end !== 'undefined' && 948 this.label.localizedLabelMargin.end.value >= 0) { 949 localizedLabelMargin.end = this.label.localizedLabelMargin.end; 950 } else if (this.hasSuffix() || this.isNeedShowCloseIconMargin()) { 951 localizedLabelMargin.end = defaultLocalizedMargin.end; 952 } 953 if (typeof this.label?.localizedLabelMargin === 'object') { 954 return localizedLabelMargin; 955 } 956 if (typeof this.label.labelMargin === 'object') { 957 const labelMargin: Margin = { left: 0, right: 0 }; 958 const defaultLabelMargin: Margin = 959 this.isSmallChipSize() ? this.theme.label.smallMargin : this.theme.label.normalMargin; 960 if (typeof this.label?.labelMargin?.left !== 'undefined' && this.isValidLength(this.label.labelMargin.left)) { 961 labelMargin.left = this.label.labelMargin.left; 962 } else if (this.hasPrefix()) { 963 labelMargin.left = defaultLabelMargin.left; 964 } 965 if (typeof this.label?.labelMargin?.right !== 'undefined' && this.isValidLength(this.label.labelMargin.right)) { 966 labelMargin.right = this.label.labelMargin.right; 967 } else if (this.hasSuffix()) { 968 labelMargin.right = defaultLabelMargin.right; 969 } 970 return labelMargin; 971 } 972 return localizedLabelMargin; 973 } 974 975 hasSuffix(): boolean { 976 if (this.suffixIcon?.src) { 977 return true; 978 } 979 return this.isChipActivated() ? !!this.suffixSymbol?.activated : !!this.suffixSymbol?.normal; 980 } 981 982 private hasPrefix(): boolean { 983 if (this.prefixIcon?.src) { 984 return true; 985 } 986 return this.isChipActivated() ? !!this.prefixSymbol?.activated : !!this.prefixSymbol?.normal; 987 } 988 989 getLabelFontWeight(): string | number | FontWeight { 990 if (this.isChipActivated()) { 991 return FontWeight.Medium; 992 } 993 return this.resourceToNumber(this.theme.label.fontWeight, FontWeight.Regular) as FontWeight; 994 } 995 996 getLabelFontFamily(): ResourceStr { 997 return this.label?.fontFamily ?? this.theme.label.fontFamily; 998 } 999 1000 private defaultSymbolFontsize(): Length { 1001 return this.isSmallChipSize() ? this.theme.defaultSymbol.smallSymbolFontSize : 1002 this.theme.defaultSymbol.normalSymbolFontSize; 1003 } 1004 1005 private getActiveFontColor(): ResourceColor { 1006 return this.chipNodeInFocus ? this.theme.label.focusActiveFontColor : this.theme.label.activatedFontColor; 1007 } 1008 1009 private getFontColor(): ResourceColor { 1010 return this.chipNodeInFocus ? this.theme.label.focusFontColor : this.theme.label.fontColor; 1011 } 1012 1013 private getChipNodeBorderColor(): ResourceColor { 1014 let themeChipNode = this.theme.chipNode; 1015 return this.isChipActivated() ? themeChipNode.activatedBorderColor : themeChipNode.borderColor; 1016 } 1017 1018 getLabelFontColor(): ResourceColor { 1019 if (this.isChipActivated()) { 1020 return this.label?.activatedFontColor ?? this.getActiveFontColor(); 1021 } 1022 return this.label?.fontColor ?? this.getFontColor(); 1023 } 1024 1025 getLabelFontSize(): Dimension { 1026 if (typeof this.label.fontSize !== 'undefined' && this.isValidLength(this.label.fontSize)) { 1027 return this.label.fontSize; 1028 } 1029 if (this.isSmallChipSize()) { 1030 return this.theme.label.smallFontSize; 1031 } 1032 return this.theme.label.normalFontSize; 1033 } 1034 1035 getChipText(): ResourceStr { 1036 return this.label?.text ?? ''; 1037 } 1038 1039 deleteChip() { 1040 animateTo({ curve: Curve.Sharp, duration: 150 }, () => { 1041 this.chipOpacity = 0; 1042 }); 1043 animateTo({ 1044 curve: Curve.FastOutLinearIn, 1045 duration: 150, 1046 onFinish: () => { 1047 this.isChipExist = false; 1048 } 1049 }, () => { 1050 this.chipScale = { x: 0.85, y: 0.85 }; 1051 }) 1052 } 1053 1054 getChipOnClicked(): Callback<ClickEvent> | undefined { 1055 if (this.onClicked) { 1056 return this.onClicked.bind(this); 1057 } 1058 return void 0; 1059 } 1060 1061 private getAccessibilitySelected(): boolean | undefined { 1062 if (this.getChipAccessibilitySelectedType() === AccessibilitySelectedType.SELECTED) { 1063 return this.isChipActivated(); 1064 } 1065 return void 0; 1066 } 1067 1068 private getAccessibilityChecked(): boolean | undefined { 1069 if (this.getChipAccessibilitySelectedType() === AccessibilitySelectedType.CHECKED) { 1070 return this.isChipActivated(); 1071 } 1072 return void 0; 1073 } 1074 1075 private getChipAccessibilitySelectedType(): AccessibilitySelectedType { 1076 if (typeof this.chipActivated === 'undefined') { 1077 return AccessibilitySelectedType.CLICKED; 1078 } 1079 return this.chipAccessibilitySelectedType ?? AccessibilitySelectedType.CHECKED; 1080 } 1081 1082 private getAccessibilityLevel(): string | undefined { 1083 return this.chipAccessibilityLevel; 1084 } 1085 1086 private getAccessibilityDescription(): Resource | undefined { 1087 if (typeof this.chipAccessibilityDescription === 'undefined') { 1088 return void 0; 1089 } 1090 return this.chipAccessibilityDescription as ESObject as Resource; 1091 } 1092 1093 isChipEnabled(): boolean { 1094 return this.chipEnabled ?? true; 1095 } 1096 1097 getChipBorderRadius(): Dimension { 1098 if (typeof this.chipNodeRadius !== 'undefined' && this.isValidLength(this.chipNodeRadius)) { 1099 return this.chipNodeRadius; 1100 } 1101 return this.isSmallChipSize() ? this.theme.chipNode.smallBorderRadius : this.theme.chipNode.normalBorderRadius; 1102 } 1103 1104 isSmallChipSize() { 1105 return typeof this.chipSize === 'string' && this.chipSize === ChipSize.SMALL; 1106 } 1107 1108 getChipBackgroundColor(): ResourceColor { 1109 let themeChipNode = this.theme.chipNode; 1110 if (this.isChipActivated()) { 1111 return this.chipNodeInFocus && !this.isSetActiveChipBgColor() ? themeChipNode.focusActivatedBgColor : 1112 this.getColor(this.chipNodeActivatedBackgroundColor, themeChipNode.activatedBackgroundColor); 1113 } 1114 return this.chipNodeInFocus && !this.isSetNormalChipBgColor() ? themeChipNode.focusBgColor : 1115 this.getColor(this.chipNodeBackgroundColor, this.theme.chipNode.backgroundColor); 1116 } 1117 1118 getColor(color: ResourceColor | undefined, defaultColor: ResourceColor): ResourceColor { 1119 if (!color) { 1120 return defaultColor; 1121 } 1122 try { 1123 ColorMetrics.resourceColor(color).color; 1124 return color; 1125 } catch (e) { 1126 console.error(`[Chip] failed to get color`); 1127 return Color.Transparent; 1128 } 1129 } 1130 1131 isChipActivated() { 1132 return this.chipActivated ?? false; 1133 } 1134 1135 resourceToNumber(resource: Resource, defaultValue: number): number { 1136 if (!resource || !resource.type) { 1137 console.error('[Chip] failed: resource get fail'); 1138 return defaultValue; 1139 } 1140 const resourceManager = this.getUIContext().getHostContext()?.resourceManager; 1141 if (!resourceManager) { 1142 console.error('[Chip] failed to get resourceManager'); 1143 return defaultValue; 1144 } 1145 switch (resource.type) { 1146 case RESOURCE_TYPE_FLOAT: 1147 case RESOURCE_TYPE_INTEGER: 1148 try { 1149 if (resource.id !== -1) { 1150 return resourceManager.getNumber(resource); 1151 } 1152 return resourceManager.getNumberByName((resource.params as string[])[0].split('.')[2]); 1153 } catch (error) { 1154 console.error(`[Chip] get resource error, return defaultValue`); 1155 return defaultValue; 1156 } 1157 default: 1158 return defaultValue; 1159 } 1160 } 1161 1162 isValidLength(length: Length): boolean { 1163 if (typeof length === 'number') { 1164 return length >= 0; 1165 } else if (typeof length === 'string') { 1166 return this.isValidLengthString(length); 1167 } else if (typeof length === 'object') { 1168 const resource = length as Resource; 1169 const resourceManager = this.getUIContext().getHostContext()?.resourceManager; 1170 if (!resourceManager) { 1171 console.error('[Chip] failed to get resourceManager.'); 1172 return false; 1173 } 1174 switch (resource.type) { 1175 case RESOURCE_TYPE_FLOAT: 1176 case RESOURCE_TYPE_INTEGER: 1177 return resourceManager.getNumber(resource) >= 0; 1178 case RESOURCE_TYPE_STRING: 1179 return this.isValidLengthString(resourceManager.getStringSync(resource)); 1180 default: 1181 return false; 1182 } 1183 } 1184 return false; 1185 } 1186 1187 isValidLengthString(length: string): boolean { 1188 const matches = length.match(/(-?\d+(?:\.\d+)?)_?(fp|vp|px|lpx)?$/i); 1189 if (!matches || matches.length < 3) { 1190 return false; 1191 } 1192 return Number.parseInt(matches[1], 10) >= 0; 1193 } 1194 1195 private chipZoomOut() { 1196 if (this.isSuffixIconFocusStyleCustomized) { 1197 this.chipScale = { 1198 x: 1, y: 1 1199 }; 1200 } 1201 } 1202 1203 private chipZoomIn() { 1204 if (this.isSuffixIconFocusStyleCustomized) { 1205 this.chipScale = { 1206 x: this.resourceToNumber(this.theme.chipNode.focusBtnScaleX, 1), 1207 y: this.resourceToNumber(this.theme.chipNode.focusBtnScaleY, 1), 1208 }; 1209 } 1210 } 1211 1212 private isNeedShowCloseIconMargin(): boolean { 1213 return this.isClosable() && this.isSuffixIconFocusStyleCustomized; 1214 } 1215 1216 build() { 1217 if (this.isChipExist) { 1218 this.ChipBuilder() 1219 } 1220 } 1221} 1222