• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
16const DEFAULT_BAR_WIDTH: number = 96;
17const DEFAULT_BAR_HEIGHT: number = 48;
18const TEXT_WIDTH_HEIGHT_SIZE: number = 24;
19const TEXT_FONT_WEIGHT: number = 500;
20const TEXT_LIGHT_HEIGHT: number = 14;
21
22@Component
23export struct AtomicServiceTabs {
24  @BuilderParam tabContents?: [TabContentBuilder?,
25                              TabContentBuilder?,
26                              TabContentBuilder?,
27                              TabContentBuilder?,
28                              TabContentBuilder?];
29  @Prop tabBarOptionsArray: [TabBarOptions, TabBarOptions, TabBarOptions?, TabBarOptions?, TabBarOptions?];
30  @Prop @Watch('barPositionChangeBySingleMode') tabBarPosition?: TabBarPosition = TabBarPosition.BOTTOM;
31  @Prop barBackgroundColor?: ResourceColor = Color.Transparent;
32  @Prop index?: number | undefined = 0;
33  @Prop barOverlap?: boolean = true;
34  @Prop layoutMode?: LayoutMode = LayoutMode.VERTICAL;
35  controller?: TabsController = new TabsController();
36  onChange?: Callback<number>;
37  onTabBarClick?: Callback<number>;
38  onContentWillChange?: OnContentWillChangeCallback;
39  @State private selectedIndex: number = 0;
40  @State private barModeStatus?: BarMode = BarMode.Fixed;
41  @State private tabBarHeight?: Length = undefined;
42  private isIconTextExist: boolean = false;
43
44  aboutToAppear(): void {
45    if (this.tabBarOptionsArray[0].icon && this.tabBarOptionsArray[0].text) {
46      this.isIconTextExist = true;
47    }
48    this.barPositionChangeBySingleMode();
49  }
50
51  /**
52   *单图标或文本场景下监听位置变化影响tabbar高度布局样式
53   */
54  barPositionChangeBySingleMode(): void {
55    if (this.isIconTextExist) {
56      return;
57    }
58    if (this.tabBarPosition === TabBarPosition.LEFT) {
59      this.tabBarHeight = (50 / this.tabBarOptionsArray.length + '%');
60      this.barModeStatus = BarMode.Scrollable;
61    } else {
62      this.barModeStatus = BarMode.Fixed;
63      this.tabBarHeight = undefined;
64    }
65  }
66
67  @Builder
68  TabBuilder(item: TabBarOptions, index: number) {
69    Flex({
70      alignItems: ItemAlign.Center,
71      justifyContent: FlexAlign.Center
72    }){
73      if (item.icon) {
74        Image(item.icon as ResourceStr)
75          .width(TEXT_WIDTH_HEIGHT_SIZE)
76          .height(TEXT_WIDTH_HEIGHT_SIZE)
77          .objectFit(ImageFit.Contain)
78          .fillColor(this.selectedIndex === index ? item.selectedColor : item.unselectedColor)
79          .backgroundColor(Color.Transparent)
80          .flexShrink(0)
81      } else {
82        Text(item.text)
83          .textOverflow({ overflow: TextOverflow.Ellipsis })
84          .maxLines(1)
85          .fontColor(this.selectedIndex === index ? item.selectedColor : item.unselectedColor)
86          .maxFontSize($r('sys.float.ohos_id_text_size_button3'))
87          .minFontSize(9)
88          .fontWeight(TEXT_FONT_WEIGHT)
89          .lineHeight(TEXT_LIGHT_HEIGHT)
90          .textAlign(TextAlign.Center)
91          .focusOnTouch(true)
92          .backgroundColor(Color.Transparent)
93      }
94    }
95    .height(this.tabBarHeight)
96  }
97
98  build() {
99    Tabs({
100      barPosition: this.tabBarPosition === TabBarPosition.LEFT ? BarPosition.Start : BarPosition.End,
101      index: this.index,
102      controller: this.controller
103    }) {
104      ForEach(this.tabBarOptionsArray, (item: TabBarOptions, index: number) => {
105        if (item) {
106          TabContent() {
107            if (this.tabContents && this.tabContents[index]) {
108              this.tabContents[index]?.()
109            }
110          }
111          .tabBar(
112            this.isIconTextExist ?
113            BottomTabBarStyle.of(item.icon, item.text).layoutMode(this.layoutMode)
114              .labelStyle({ unselectedColor: item.unselectedColor, selectedColor: item.selectedColor })
115              .iconStyle({ unselectedColor: item.unselectedColor, selectedColor: item.selectedColor }) :
116            this.TabBuilder(item, index)
117          )
118          .width((!this.tabContents && this.tabBarPosition === TabBarPosition.LEFT) ? DEFAULT_BAR_WIDTH : '100%')
119          .height((!this.tabContents && this.tabBarPosition === TabBarPosition.BOTTOM) ? DEFAULT_BAR_HEIGHT : '100%')
120        }
121      })
122    }
123    .safeAreaPadding({
124      // barHeight设置具体高度时,tabBar的背景色不会延伸到底部安全区,需要新增该属性值使tabBar和安全区背景色一致
125      bottom: 0
126    })
127    .animationDuration(0) // 切换页签时,tabBar有默认的动画效果,设置该属性取消动画效果
128    .barBackgroundColor(this.barBackgroundColor)
129    .divider(null)
130    .barMode(this.barModeStatus)
131    .vertical(this.tabBarPosition === TabBarPosition.LEFT ? true : false)
132    .scrollable(false)
133    .barOverlap(this.barOverlap)
134    .barBackgroundBlurStyle(BlurStyle.COMPONENT_THICK)
135    .onChange((index: number) => {
136      if (this.onChange) {
137        this.onChange(index);
138      }
139      this.selectedIndex = index;
140    })
141    .onTabBarClick(this.onTabBarClick)
142    .onContentWillChange(this.onContentWillChange)
143    .barWidth(this.isIconTextExist ? undefined :
144      (this.tabBarPosition === TabBarPosition.LEFT) ? DEFAULT_BAR_WIDTH : '100%')
145    .barHeight(this.isIconTextExist ? undefined :
146      (this.tabBarPosition === TabBarPosition.BOTTOM) ? DEFAULT_BAR_HEIGHT : '100%')
147    .width((!this.tabContents && this.tabBarPosition === TabBarPosition.LEFT) ? DEFAULT_BAR_WIDTH : '100%')
148    .height((!this.tabContents && this.tabBarPosition === TabBarPosition.BOTTOM) ? DEFAULT_BAR_HEIGHT : '100%')
149  }
150}
151
152export class TabBarOptions {
153  public icon: ResourceStr | TabBarSymbol;
154  public text: ResourceStr;
155  public unselectedColor?: ResourceColor;
156  public selectedColor?: ResourceColor;
157
158  constructor(icon: ResourceStr | TabBarSymbol, text: ResourceStr,
159              unselectedColor?: ResourceColor, selectedColor?: ResourceColor) {
160    this.icon = icon;
161    this.text = text;
162    this.unselectedColor = unselectedColor;
163    this.selectedColor = selectedColor;
164  }
165}
166
167export enum TabBarPosition {
168  LEFT = 0,
169  BOTTOM = 1
170}
171
172export type TabContentBuilder = () => void;
173export type OnContentWillChangeCallback = (currentIndex: number, comingIndex: number) => boolean;