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