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 this.chipScale = { 625 x: this.resourceToNumber(this.theme.chipNode.focusBtnScaleX, 1), 626 y: this.resourceToNumber(this.theme.chipNode.focusBtnScaleY, 1), 627 }; 628 } 629 }) 630 .onBlur(() => { 631 if (this.isSuffixIconFocusStyleCustomized) { 632 this.chipNodeInFocus = false; 633 this.chipScale = { 634 x: 1, y: 1 635 }; 636 } 637 }) 638 } 639 640 private getCloseIconAccessibilityLevel(): string { 641 if (this.closeOptions?.accessibilityLevel === 'no' || this.closeOptions?.accessibilityLevel === 'no-hide-descendants') { 642 return this.closeOptions.accessibilityLevel; 643 } 644 return 'yes'; 645 } 646 647 private getCloseIconAccessibilityDescription(): Resource | undefined { 648 if (typeof this.closeOptions?.accessibilityDescription === 'undefined') { 649 return void 0; 650 } 651 return this.closeOptions.accessibilityDescription as Resource; 652 } 653 654 private getCloseIconAccessibilityText(): Resource { 655 if (typeof this.closeOptions?.accessibilityText === 'undefined') { 656 return $r('sys.string.delete_used_for_accessibility_text'); 657 } 658 return this.closeOptions.accessibilityText as ESObject as Resource; 659 } 660 661 getSuffixIconAction(): Callback<ClickEvent> | undefined { 662 if (this.suffixIcon?.src) { 663 if (!this.suffixIcon?.action) { 664 return void 0; 665 } 666 return () => { 667 if (this.isChipEnabled()) { 668 this.suffixIcon?.action?.(); 669 } 670 }; 671 } 672 return void 0; 673 } 674 675 getSuffixIconFilledColor(): ResourceColor { 676 if (this.isChipActivated()) { 677 return this.suffixIcon?.activatedFillColor ?? this.getDefaultActiveIconColor(IconType.PREFIX_ICON); 678 } 679 return this.suffixIcon?.fillColor ?? this.getDefaultFillIconColor(IconType.SUFFIX_ICON); 680 } 681 682 getSuffixIconSize(): SizeOptions { 683 let suffixIconSize: SizeOptions = { width: 0, height: 0 }; 684 if (typeof this.suffixIcon?.size?.width !== 'undefined' && this.isValidLength(this.suffixIcon.size.width)) { 685 suffixIconSize.width = this.suffixIcon.size.width; 686 } else { 687 suffixIconSize.width = 688 this.isSmallChipSize() ? this.theme.suffixIcon.smallSize.width : this.theme.suffixIcon.normalSize.width; 689 } 690 if (typeof this.suffixIcon?.size?.height !== 'undefined' && this.isValidLength(this.suffixIcon.size.height)) { 691 suffixIconSize.height = this.suffixIcon.size.height; 692 } else { 693 suffixIconSize.height = 694 this.isSmallChipSize() ? this.theme.suffixIcon.smallSize.height : this.theme.suffixIcon.normalSize.height; 695 } 696 return suffixIconSize; 697 } 698 699 getSuffixIconAccessibilityLevel(): string { 700 if (this.suffixIcon?.accessibilityLevel === 'no' || this.suffixIcon?.accessibilityLevel === 'no-hide-descendants') { 701 return this.suffixIcon.accessibilityLevel; 702 } 703 return this.suffixIcon?.action ? 'yes' : 'no'; 704 } 705 706 getSuffixIconAccessibilityDescription(): Resource | undefined { 707 if (typeof this.suffixIcon?.accessibilityDescription === 'undefined') { 708 return void 0; 709 } 710 return this.suffixIcon.accessibilityDescription as ESObject as Resource; 711 } 712 713 getSuffixIconAccessibilityText(): Resource | undefined { 714 if (typeof this.suffixIcon?.accessibilityText === 'undefined') { 715 return void 0; 716 } 717 718 return this.suffixIcon.accessibilityText as ESObject as Resource; 719 } 720 721 isClosable(): boolean { 722 return this.allowClose ?? true; 723 } 724 725 getSuffixSymbolModifier(): SymbolGlyphModifier | undefined { 726 if (this.isChipActivated()) { 727 return this.suffixSymbol?.activated; 728 } 729 return this.suffixSymbol?.normal; 730 } 731 732 getSuffixSymbolAccessibilityLevel(): string { 733 if (this.isChipActivated()) { 734 if (this.suffixSymbolOptions?.activatedAccessibility?.accessibilityLevel === 'no' || 735 this.suffixSymbolOptions?.activatedAccessibility?.accessibilityLevel === 'no-hide-descendants') { 736 return this.suffixSymbolOptions.activatedAccessibility.accessibilityLevel; 737 } 738 return this.suffixSymbolOptions?.action ? 'yes' : 'no'; 739 } 740 if (this.suffixSymbolOptions?.normalAccessibility?.accessibilityLevel === 'no' || 741 this.suffixSymbolOptions?.normalAccessibility?.accessibilityLevel === 'no-hide-descendants') { 742 return this.suffixSymbolOptions.normalAccessibility.accessibilityLevel; 743 } 744 return this.suffixSymbolOptions?.action ? 'yes' : 'no'; 745 } 746 747 getSuffixSymbolAccessibilityDescription(): Resource | undefined { 748 if (this.isChipActivated()) { 749 if (typeof this.suffixSymbolOptions?.activatedAccessibility?.accessibilityDescription !== 'undefined') { 750 return this.suffixSymbolOptions.activatedAccessibility.accessibilityDescription as Resource; 751 } 752 return void 0; 753 } 754 if (typeof this.suffixSymbolOptions?.normalAccessibility?.accessibilityDescription !== 'undefined') { 755 return this.suffixSymbolOptions.normalAccessibility.accessibilityDescription as Resource; 756 } 757 return void 0; 758 } 759 760 getSuffixSymbolAccessibilityText(): Resource | undefined { 761 if (this.isChipActivated()) { 762 if (typeof this.suffixSymbolOptions?.activatedAccessibility?.accessibilityText !== 'undefined') { 763 return this.suffixSymbolOptions.activatedAccessibility.accessibilityText as Resource; 764 } 765 return void 0; 766 } 767 if (typeof this.suffixSymbolOptions?.normalAccessibility?.accessibilityText !== 'undefined') { 768 return this.suffixSymbolOptions.normalAccessibility.accessibilityText as Resource; 769 } 770 return void 0; 771 } 772 773 getSuffixSymbolAction(): Callback<ClickEvent> | undefined { 774 if (typeof this.suffixSymbolOptions?.action === 'undefined') { 775 return void 0; 776 } 777 return () => { 778 if (!this.isChipEnabled()) { 779 return; 780 } 781 this.suffixSymbolOptions?.action?.(); 782 }; 783 } 784 785 hasSuffixSymbol(): boolean { 786 return !!(this.suffixSymbol?.normal || this.suffixSymbol?.activated); 787 } 788 789 getPrefixIconFilledColor(): ResourceColor { 790 if (this.isChipActivated()) { 791 return this.prefixIcon?.activatedFillColor ?? this.getDefaultActiveIconColor(IconType.PREFIX_ICON); 792 } 793 return this.prefixIcon?.fillColor ?? this.getDefaultFillIconColor(IconType.PREFIX_ICON); 794 } 795 796 getPrefixIconSize(): SizeOptions { 797 let prefixIconSize: SizeOptions = { width: 0, height: 0 }; 798 if (typeof this.prefixIcon?.size?.width !== 'undefined' && this.isValidLength(this.prefixIcon.size.width)) { 799 prefixIconSize.width = this.prefixIcon.size.width; 800 } else { 801 prefixIconSize.width = 802 this.isSmallChipSize() ? this.theme.prefixIcon.smallSize.width : this.theme.prefixIcon.normalSize.width; 803 } 804 if (typeof this.prefixIcon?.size?.height !== 'undefined' && this.isValidLength(this.prefixIcon.size.height)) { 805 prefixIconSize.height = this.prefixIcon.size.height; 806 } else { 807 prefixIconSize.height = 808 this.isSmallChipSize() ? this.theme.prefixIcon.smallSize.height : this.theme.prefixIcon.normalSize.height; 809 } 810 return prefixIconSize; 811 } 812 813 getPrefixSymbolModifier(): SymbolGlyphModifier | undefined { 814 if (this.isChipActivated()) { 815 return this.prefixSymbol?.activated; 816 } 817 return this.prefixSymbol?.normal; 818 } 819 820 getDefaultSymbolColor(iconType: string): ResourceColor[] { 821 return this.isChipActivated() ? this.getSymbolActiveColor(iconType) : 822 this.getSymbolFillColor(iconType); 823 } 824 825 private getDefaultActiveIconColor(iconType: string): ResourceColor { 826 if (iconType === IconType.PREFIX_ICON) { 827 return this.chipNodeInFocus ? this.theme.prefixIcon.focusActivatedColor : 828 this.theme.prefixIcon.activatedFillColor; 829 } else { 830 return this.chipNodeInFocus ? this.theme.suffixIcon.focusActivatedColor : 831 this.theme.suffixIcon.activatedFillColor; 832 } 833 } 834 835 private getDefaultFillIconColor(iconType: string): ResourceColor { 836 if (iconType === IconType.PREFIX_ICON) { 837 return this.chipNodeInFocus ? this.theme.prefixIcon.focusFillColor : this.theme.prefixIcon.fillColor; 838 } else { 839 return this.chipNodeInFocus ? this.theme.suffixIcon.focusFillColor : this.theme.suffixIcon.fillColor; 840 } 841 } 842 843 private getSymbolActiveColor(iconType: string): ResourceColor[] { 844 if (!this.chipNodeInFocus) { 845 return this.theme.defaultSymbol.activatedFontColor; 846 } 847 if (iconType === IconType.PREFIX_SYMBOL) { 848 return [this.theme.prefixIcon.focusActivatedColor]; 849 } 850 if (iconType === IconType.SUFFIX_SYMBOL) { 851 return [this.theme.suffixIcon.focusActivatedColor]; 852 } 853 return this.theme.defaultSymbol.activatedFontColor; 854 } 855 856 private getSymbolFillColor(iconType?: string): ResourceColor[] { 857 if (!this.chipNodeInFocus) { 858 return this.theme.defaultSymbol.normalFontColor; 859 } 860 if (iconType === IconType.PREFIX_SYMBOL) { 861 return [this.theme.prefixIcon.focusFillColor]; 862 } 863 if (iconType === IconType.SUFFIX_SYMBOL) { 864 return [this.theme.suffixIcon.focusFillColor]; 865 } 866 return this.theme.defaultSymbol.normalFontColor; 867 } 868 869 hasPrefixSymbol(): boolean { 870 return !!(this.prefixSymbol?.normal || this.prefixSymbol?.activated); 871 } 872 873 getChipConstraintSize(): ConstraintSizeOptions | undefined { 874 const constraintSize: ConstraintSizeOptions = {}; 875 if (typeof this.chipSize === 'string') { 876 constraintSize.maxWidth = this.getChipMaxWidth(); 877 constraintSize.minHeight = 878 this.chipSize === ChipSize.SMALL ? this.theme.chipNode.smallHeight : this.theme.chipNode.normalHeight; 879 } else { 880 if (typeof this.chipSize?.width === 'undefined' || !this.isValidLength(this.chipSize.width)) { 881 constraintSize.maxWidth = this.getChipMaxWidth(); 882 } 883 if (typeof this.chipSize?.height === 'undefined' || !this.isValidLength(this.chipSize.height)) { 884 constraintSize.minHeight = this.theme.chipNode.normalHeight; 885 } 886 } 887 return constraintSize; 888 } 889 890 getChipMaxWidth(): Length | undefined { 891 if (this.fontSizeScale >= this.theme.chipNode.suitAgeScale) { 892 return void 0; 893 } 894 if (this.breakPoint === BreakPointsType.SM) { 895 return this.theme.chipNode.breakPointConstraintWidth.breakPointSmMaxWidth; 896 } 897 if (this.breakPoint === BreakPointsType.MD) { 898 return this.theme.chipNode.breakPointConstraintWidth.breakPointMdMaxWidth; 899 } 900 if (this.breakPoint === BreakPointsType.LG) { 901 return this.theme.chipNode.breakPointConstraintWidth.breakPointLgMaxWidth; 902 } 903 return void 0; 904 } 905 906 getChipSize(): SizeOptions | undefined { 907 const chipSize: SizeOptions = { 908 width: 'auto', 909 height: 'auto' 910 }; 911 912 if (typeof this.chipSize !== 'string') { 913 if (typeof this.chipSize?.width !== 'undefined' && this.isValidLength(this.chipSize.width)) { 914 chipSize.width = this.chipSize.width; 915 } 916 if (typeof this.chipSize?.height !== 'undefined' && this.isValidLength(this.chipSize.height)) { 917 chipSize.height = this.chipSize.height; 918 } 919 } 920 921 return chipSize; 922 } 923 924 getChipPadding(): Length | Padding | LocalizedPadding { 925 return this.isSmallChipSize() ? this.theme.chipNode.localizedSmallPadding : 926 this.theme.chipNode.localizedNormalPadding; 927 } 928 929 getLabelMargin(): Length | Padding | LocalizedPadding { 930 const localizedLabelMargin: LocalizedMargin = { 931 start: LengthMetrics.vp(0), 932 end: LengthMetrics.vp(0), 933 }; 934 const defaultLocalizedMargin = 935 this.isSmallChipSize() ? this.theme.label.localizedSmallMargin : this.theme.label.localizedNormalMargin; 936 937 if (typeof this.label?.localizedLabelMargin?.start !== 'undefined' && 938 this.label.localizedLabelMargin.start.value >= 0) { 939 localizedLabelMargin.start = this.label.localizedLabelMargin.start; 940 } else if (this.hasPrefix()) { 941 localizedLabelMargin.start = defaultLocalizedMargin.start; 942 } 943 944 if (typeof this.label?.localizedLabelMargin?.end !== 'undefined' && 945 this.label.localizedLabelMargin.end.value >= 0) { 946 localizedLabelMargin.end = this.label.localizedLabelMargin.end; 947 } else if (this.hasSuffix()) { 948 localizedLabelMargin.end = defaultLocalizedMargin.end; 949 } 950 if (typeof this.label?.localizedLabelMargin === 'object') { 951 return localizedLabelMargin; 952 } 953 if (typeof this.label.labelMargin === 'object') { 954 const labelMargin: Margin = { left: 0, right: 0 }; 955 const defaultLabelMargin: Margin = 956 this.isSmallChipSize() ? this.theme.label.smallMargin : this.theme.label.normalMargin; 957 if (typeof this.label?.labelMargin?.left !== 'undefined' && this.isValidLength(this.label.labelMargin.left)) { 958 labelMargin.left = this.label.labelMargin.left; 959 } else if (this.hasPrefix()) { 960 labelMargin.left = defaultLabelMargin.left; 961 } 962 if (typeof this.label?.labelMargin?.right !== 'undefined' && this.isValidLength(this.label.labelMargin.right)) { 963 labelMargin.right = this.label.labelMargin.right; 964 } else if (this.hasSuffix()) { 965 labelMargin.right = defaultLabelMargin.right; 966 } 967 return labelMargin; 968 } 969 return localizedLabelMargin; 970 } 971 972 hasSuffix(): boolean { 973 if (this.suffixIcon?.src) { 974 return true; 975 } 976 return this.isChipActivated() ? !!this.suffixSymbol?.activated : !!this.suffixSymbol?.normal; 977 } 978 979 private hasPrefix(): boolean { 980 if (this.prefixIcon?.src) { 981 return true; 982 } 983 return this.isChipActivated() ? !!this.prefixSymbol?.activated : !!this.prefixSymbol?.normal; 984 } 985 986 getLabelFontWeight(): string | number | FontWeight { 987 if (this.isChipActivated()) { 988 return FontWeight.Medium; 989 } 990 return this.resourceToNumber(this.theme.label.fontWeight, FontWeight.Regular) as FontWeight; 991 } 992 993 getLabelFontFamily(): ResourceStr { 994 return this.label?.fontFamily ?? this.theme.label.fontFamily; 995 } 996 997 private defaultSymbolFontsize(): Length { 998 return this.isSmallChipSize() ? this.theme.defaultSymbol.smallSymbolFontSize : 999 this.theme.defaultSymbol.normalSymbolFontSize; 1000 } 1001 1002 private getActiveFontColor(): ResourceColor { 1003 return this.chipNodeInFocus ? this.theme.label.focusActiveFontColor : this.theme.label.activatedFontColor; 1004 } 1005 1006 private getFontColor(): ResourceColor { 1007 return this.chipNodeInFocus ? this.theme.label.focusFontColor : this.theme.label.fontColor; 1008 } 1009 1010 private getChipNodeBorderColor(): ResourceColor { 1011 let themeChipNode = this.theme.chipNode; 1012 return this.isChipActivated() ? themeChipNode.activatedBorderColor : themeChipNode.borderColor; 1013 } 1014 1015 getLabelFontColor(): ResourceColor { 1016 if (this.isChipActivated()) { 1017 return this.label?.activatedFontColor ?? this.getActiveFontColor(); 1018 } 1019 return this.label?.fontColor ?? this.getFontColor(); 1020 } 1021 1022 getLabelFontSize(): Dimension { 1023 if (typeof this.label.fontSize !== 'undefined' && this.isValidLength(this.label.fontSize)) { 1024 return this.label.fontSize; 1025 } 1026 if (this.isSmallChipSize()) { 1027 return this.theme.label.smallFontSize; 1028 } 1029 return this.theme.label.normalFontSize; 1030 } 1031 1032 getChipText(): ResourceStr { 1033 return this.label?.text ?? ''; 1034 } 1035 1036 deleteChip() { 1037 animateTo({ curve: Curve.Sharp, duration: 150 }, () => { 1038 this.chipOpacity = 0; 1039 }); 1040 animateTo({ 1041 curve: Curve.FastOutLinearIn, 1042 duration: 150, 1043 onFinish: () => { 1044 this.isChipExist = false; 1045 } 1046 }, () => { 1047 this.chipScale = { x: 0.85, y: 0.85 }; 1048 }) 1049 } 1050 1051 getChipOnClicked(): Callback<ClickEvent> | undefined { 1052 if (this.onClicked) { 1053 return this.onClicked.bind(this); 1054 } 1055 return void 0; 1056 } 1057 1058 private getAccessibilitySelected(): boolean | undefined { 1059 if (this.getChipAccessibilitySelectedType() === AccessibilitySelectedType.SELECTED) { 1060 return this.isChipActivated(); 1061 } 1062 return void 0; 1063 } 1064 1065 private getAccessibilityChecked(): boolean | undefined { 1066 if (this.getChipAccessibilitySelectedType() === AccessibilitySelectedType.CHECKED) { 1067 return this.isChipActivated(); 1068 } 1069 return void 0; 1070 } 1071 1072 private getChipAccessibilitySelectedType(): AccessibilitySelectedType { 1073 if (typeof this.chipActivated === 'undefined') { 1074 return AccessibilitySelectedType.CLICKED; 1075 } 1076 return this.chipAccessibilitySelectedType ?? AccessibilitySelectedType.CHECKED; 1077 } 1078 1079 private getAccessibilityLevel(): string | undefined { 1080 return this.chipAccessibilityLevel; 1081 } 1082 1083 private getAccessibilityDescription(): Resource | undefined { 1084 if (typeof this.chipAccessibilityDescription === 'undefined') { 1085 return void 0; 1086 } 1087 return this.chipAccessibilityDescription as ESObject as Resource; 1088 } 1089 1090 isChipEnabled(): boolean { 1091 return this.chipEnabled ?? true; 1092 } 1093 1094 getChipBorderRadius(): Dimension { 1095 if (typeof this.chipNodeRadius !== 'undefined' && this.isValidLength(this.chipNodeRadius)) { 1096 return this.chipNodeRadius; 1097 } 1098 return this.isSmallChipSize() ? this.theme.chipNode.smallBorderRadius : this.theme.chipNode.normalBorderRadius; 1099 } 1100 1101 isSmallChipSize() { 1102 return typeof this.chipSize === 'string' && this.chipSize === ChipSize.SMALL; 1103 } 1104 1105 getChipBackgroundColor(): ResourceColor { 1106 let themeChipNode = this.theme.chipNode; 1107 if (this.isChipActivated()) { 1108 return this.chipNodeInFocus && !this.isSetActiveChipBgColor() ? themeChipNode.focusActivatedBgColor : 1109 this.getColor(this.chipNodeActivatedBackgroundColor, themeChipNode.activatedBackgroundColor); 1110 } 1111 return this.chipNodeInFocus && !this.isSetNormalChipBgColor() ? themeChipNode.focusBgColor : 1112 this.getColor(this.chipNodeBackgroundColor, this.theme.chipNode.backgroundColor); 1113 } 1114 1115 getColor(color: ResourceColor | undefined, defaultColor: ResourceColor): ResourceColor { 1116 if (!color) { 1117 return defaultColor; 1118 } 1119 try { 1120 ColorMetrics.resourceColor(color).color; 1121 return color; 1122 } catch (e) { 1123 console.error(`[Chip] failed to get color`); 1124 return Color.Transparent; 1125 } 1126 } 1127 1128 isChipActivated() { 1129 return this.chipActivated ?? false; 1130 } 1131 1132 resourceToNumber(resource: Resource, defaultValue: number): number { 1133 if (!resource || !resource.type) { 1134 console.error('[Chip] failed: resource get fail'); 1135 return defaultValue; 1136 } 1137 const resourceManager = this.getUIContext().getHostContext()?.resourceManager; 1138 if (!resourceManager) { 1139 console.error('[Chip] failed to get resourceManager'); 1140 return defaultValue; 1141 } 1142 switch (resource.type) { 1143 case RESOURCE_TYPE_FLOAT: 1144 case RESOURCE_TYPE_INTEGER: 1145 try { 1146 if (resource.id !== -1) { 1147 return resourceManager.getNumber(resource); 1148 } 1149 return resourceManager.getNumberByName((resource.params as string[])[0].split('.')[2]); 1150 } catch (error) { 1151 console.error(`[Chip] get resource error, return defaultValue`); 1152 return defaultValue; 1153 } 1154 default: 1155 return defaultValue; 1156 } 1157 } 1158 1159 isValidLength(length: Length): boolean { 1160 if (typeof length === 'number') { 1161 return length >= 0; 1162 } else if (typeof length === 'string') { 1163 return this.isValidLengthString(length); 1164 } else if (typeof length === 'object') { 1165 const resource = length as Resource; 1166 const resourceManager = this.getUIContext().getHostContext()?.resourceManager; 1167 if (!resourceManager) { 1168 console.error('[Chip] failed to get resourceManager.'); 1169 return false; 1170 } 1171 switch (resource.type) { 1172 case RESOURCE_TYPE_FLOAT: 1173 case RESOURCE_TYPE_INTEGER: 1174 return resourceManager.getNumber(resource) >= 0; 1175 case RESOURCE_TYPE_STRING: 1176 return this.isValidLengthString(resourceManager.getStringSync(resource)); 1177 default: 1178 return false; 1179 } 1180 } 1181 return false; 1182 } 1183 1184 isValidLengthString(length: string): boolean { 1185 const matches = length.match(/(-?\d+(?:\.\d+)?)_?(fp|vp|px|lpx)?$/i); 1186 if (!matches || matches.length < 3) { 1187 return false; 1188 } 1189 return Number.parseInt(matches[1], 10) >= 0; 1190 } 1191 1192 build() { 1193 if (this.isChipExist) { 1194 this.ChipBuilder() 1195 } 1196 } 1197} 1198