• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023-2023 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'
17
18const SPACE_MARGIN: number = 8
19const MARGIN_NUM: number = 4
20const IMAGE_WIDTH_NUM: number = 16
21const IMAGE_HEIGHT_NUM: number = 24
22const BUTTON_SIZE: number = 32
23const SINGLE_LINE_HEIGHT: number = 48
24const DOUBLE_LINE_HEIGHT: number = 64
25const BUTTON_HEIGHT: number = 28
26const IMAGE_WIDTH: number = 12
27const BORDER_WIDTH = 2
28const DIVIDEND_WIDTH = 3
29const SINGLE_LINE_NUM: number = 1
30const DOUBLE_LINE_NUM: number = 2
31const MIN_FONT_SIZE: number = 14
32const MAIN_TEXT_SIZE: number = 10
33const CONSTRAINT_NUM: number = 44
34const CONTENT_NUM: number = 40
35
36export enum OperationType {
37  TEXT_ARROW = 0,
38  BUTTON = 1,
39  ICON_GROUP = 2,
40  LOADING = 3,
41}
42
43export declare type OperationOption = {
44  value: ResourceStr;
45  action?: () => void;
46}
47
48export declare type SelectOptions = {
49  options: Array<SelectOption>;
50  selected?: number;
51  value?: string;
52  onSelect?: (index: number, value?: string) => void;
53}
54
55@Component
56struct IconGroup {
57  @State bgColor: Resource = $r('sys.color.ohos_id_color_sub_background_transparent')
58  @State isFocus: boolean = false
59  item: OperationOption
60  focusBorderWidth = BORDER_WIDTH
61
62  build() {
63    Row() {
64      Image(this.item.value)
65        .fillColor($r('sys.color.ohos_id_color_primary'))
66        .width(IMAGE_HEIGHT_NUM)
67        .height(IMAGE_HEIGHT_NUM)
68        .focusable(true)
69    }
70    .focusable(true)
71    .width(BUTTON_SIZE)
72    .height(BUTTON_SIZE)
73    .margin({ right: SPACE_MARGIN, bottom: MARGIN_NUM })
74    .justifyContent(FlexAlign.Center)
75    .borderRadius($r('sys.float.ohos_id_corner_radius_clicked'))
76    .backgroundColor(this.bgColor)
77    .onTouch((event) => {
78      if (event.type === TouchType.Down) {
79        this.item.action && this.item.action()
80        this.bgColor = $r('sys.color.ohos_id_color_click_effect')
81      }
82      if (event.type === TouchType.Up) {
83        this.bgColor = $r('sys.color.ohos_id_color_sub_background_transparent')
84      }
85    })
86    .onHover((isHover: boolean) => {
87      if (isHover) {
88        this.bgColor = $r('sys.color.ohos_id_color_hover')
89      } else {
90        this.bgColor = $r('sys.color.ohos_id_color_sub_background_transparent')
91      }
92    })
93    .stateStyles({
94      focused: {
95        .border({
96          radius: $r('sys.float.ohos_id_corner_radius_clicked'),
97          width: this.focusBorderWidth,
98          color: $r('sys.color.ohos_id_color_focused_outline'),
99          style: BorderStyle.Solid
100        })
101      },
102      normal: {
103        .border({
104          radius: $r('sys.float.ohos_id_corner_radius_clicked'),
105          width: 0 })
106      }
107    })
108    .onKeyEvent((event) => {
109      if (event.keyCode === KeyCode.KEYCODE_ENTER || event.keyCode === KeyCode.KEYCODE_SPACE) {
110        this.item.action && this.item.action()
111      }
112    })
113  }
114}
115
116@Component
117export struct SubHeader {
118  @Prop icon: Resource
119  @Prop primaryTitle: string
120  @Prop secondaryTitle: string
121  @Prop select: SelectOptions
122  @Prop operationType: OperationType = OperationType.BUTTON
123  operationItem: Array<OperationOption>
124  @State isDuplicateLine: boolean = false
125  @State textArrowBgColor: Resource = $r('sys.color.ohos_id_color_sub_background_transparent')
126  @State buttonBgColor: Resource = $r('sys.color.ohos_id_color_sub_background_transparent')
127  @State flexWidth: number = 0
128  @State textArrowWidth: number = 0
129  @State textArrowFocus: boolean = false
130  @State buttonFocus: boolean = false
131  @State arrowWidth: number = 0
132  @State buttonWidth: number = 0
133  focusBorderWidth = BORDER_WIDTH
134
135  @Builder ListTextStyle($$: { content: ResourceStr }) {
136    Text($$.content)
137      .fontColor($r('sys.color.ohos_id_color_text_secondary'))
138      .fontSize($r('sys.float.ohos_id_text_size_sub_title3'))
139      .fontWeight(FontWeight.Medium)
140      .maxLines(DOUBLE_LINE_NUM)
141      .textOverflow({ overflow: TextOverflow.Ellipsis })
142      .margin({ left: $r('sys.float.ohos_id_max_padding_end'), bottom: SPACE_MARGIN, right: MARGIN_NUM })
143  }
144
145  @Builder ListIconStyle($$: { content: ResourceStr }, icon: ResourceStr) {
146    Row() {
147      Image(icon)
148        .width(IMAGE_WIDTH_NUM)
149        .height(IMAGE_WIDTH_NUM)
150        .margin({ right: SPACE_MARGIN })
151      Text($$.content)
152        .fontColor($r('sys.color.ohos_id_color_text_secondary'))
153        .fontSize($r('sys.float.ohos_id_text_size_sub_title3'))
154        .fontWeight(FontWeight.Medium)
155        .maxLines(DOUBLE_LINE_NUM)
156        .textOverflow({ overflow: TextOverflow.Ellipsis })
157    }
158    .margin({ left: $r('sys.float.ohos_id_max_padding_end'), bottom: SPACE_MARGIN, right: MARGIN_NUM })
159  }
160
161  @Builder ContentTextStyle($$: { content: ResourceStr }) {
162    Text($$.content)
163      .fontColor($r('sys.color.ohos_id_color_text_primary'))
164      .fontSize($r('sys.float.ohos_id_text_size_sub_title1'))
165      .fontWeight(FontWeight.Medium)
166      .maxLines(DOUBLE_LINE_NUM)
167      .maxFontSize($r('sys.float.ohos_id_text_size_sub_title1'))
168      .minFontSize(MIN_FONT_SIZE)
169      .textOverflow({ overflow: TextOverflow.Ellipsis })
170      .margin({ left: $r('sys.float.ohos_id_max_padding_start'),
171        right: MARGIN_NUM, bottom: SPACE_MARGIN })
172  }
173
174  @Builder SubTextStyle($$: { content: ResourceStr, subContent: ResourceStr }) {
175    Column() {
176      Text($$.content)
177        .fontColor($r('sys.color.ohos_id_color_text_primary'))
178        .fontSize($r('sys.float.ohos_id_text_size_sub_title1'))
179        .fontWeight(FontWeight.Medium)
180        .maxLines(SINGLE_LINE_NUM)
181        .maxFontSize($r('sys.float.ohos_id_text_size_sub_title1'))
182        .minFontSize(MIN_FONT_SIZE)
183        .textOverflow({ overflow: TextOverflow.Ellipsis })
184      Text($$.subContent)
185        .fontColor($r('sys.color.ohos_id_color_text_secondary'))
186        .fontSize($r('sys.float.ohos_id_text_size_sub_title3'))
187        .fontWeight(FontWeight.Medium)
188        .maxLines(SINGLE_LINE_NUM)
189        .maxFontSize($r('sys.float.ohos_id_text_size_sub_title3'))
190        .minFontSize(MAIN_TEXT_SIZE)
191        .textOverflow({ overflow: TextOverflow.Ellipsis })
192    }
193    .alignItems(HorizontalAlign.Start)
194    .onAppear(() => {
195      this.isDuplicateLine = true
196    })
197    .margin({ left: $r('sys.float.ohos_id_max_padding_start'),
198      right: MARGIN_NUM, bottom: SPACE_MARGIN })
199  }
200
201  @Builder SelectStyle(selectParam: SelectOptions) {
202    Select(selectParam.options)
203      .selected(selectParam.selected)
204      .value(selectParam.value)
205      .onSelect((index: number, value?: string) => {
206        if (selectParam.onSelect) {
207          selectParam.onSelect(index, value)
208        }
209      })
210      .font({
211        size: $r('sys.float.ohos_id_text_size_sub_title1'),
212        weight: FontWeight.Medium
213      })
214      .margin({ left: $r('sys.float.ohos_id_default_padding_start'), right: MARGIN_NUM })
215  }
216
217  @Builder LoadingProcessStyle() {
218    LoadingProgress()
219      .width(IMAGE_HEIGHT_NUM)
220      .height(IMAGE_HEIGHT_NUM)
221      .focusable(true)
222      .margin({ right: $r('sys.float.ohos_id_default_padding_end'), bottom: MARGIN_NUM })
223  }
224
225  @Builder TextArrowStyle(textArrow: OperationOption) {
226    Row() {
227      Stack() {
228        Row() {
229          Row() {
230            if (textArrow != null) {
231              Text(textArrow.value)
232                .fontColor($r('sys.color.ohos_id_color_text_secondary'))
233                .fontSize($r('sys.float.ohos_id_text_size_body2'))
234                .margin({ right: MARGIN_NUM })
235                .focusable(true)
236                .maxLines(1)
237                .constraintSize({ maxWidth: this.textArrowWidth - CONTENT_NUM })
238            }
239            Image($r('sys.media.ohos_ic_public_arrow_right'))
240              .fillColor($r('sys.color.ohos_id_color_tertiary'))
241              .width(IMAGE_WIDTH)
242              .height(IMAGE_HEIGHT_NUM)
243              .focusable(true)
244          }.margin({ left: SPACE_MARGIN, right: SPACE_MARGIN })
245        }
246        .height(BUTTON_SIZE)
247        .justifyContent(FlexAlign.End)
248        .onFocus(() => {
249          this.textArrowFocus = true
250        })
251        .onBlur(() => {
252          this.textArrowFocus = false
253        })
254        .borderRadius(MARGIN_NUM)
255        .focusable(true)
256        .margin({ left: MARGIN_NUM, right: MARGIN_NUM })
257        .backgroundColor(this.textArrowBgColor)
258        .onTouch((event) => {
259          if (event.type === TouchType.Down) {
260            if (textArrow.action) {
261              textArrow.action()
262            }
263            this.textArrowBgColor = $r('sys.color.ohos_id_color_click_effect')
264          }
265          if (event.type === TouchType.Up) {
266            this.textArrowBgColor = $r('sys.color.ohos_id_color_sub_background_transparent')
267          }
268        })
269        .onHover((isHover: boolean) => {
270          if (isHover) {
271            this.textArrowBgColor = $r('sys.color.ohos_id_color_hover')
272          } else {
273            this.textArrowBgColor = $r('sys.color.ohos_id_color_sub_background_transparent')
274          }
275        })
276        .onKeyEvent((event) => {
277          if (event.keyCode === KeyCode.KEYCODE_ENTER || event.keyCode === KeyCode.KEYCODE_SPACE) {
278            textArrow.action && textArrow.action()
279          }
280        })
281        .onAreaChange((oldValue: Area, newValue: Area) => {
282          this.arrowWidth = Number(parseInt(newValue.width.toString(), 0))
283        })
284
285        if (this.textArrowFocus) {
286          Row()
287            .height(BUTTON_SIZE)
288            .width(this.arrowWidth)
289            .hitTestBehavior(HitTestMode.None)
290            .border({
291              width: BORDER_WIDTH,
292              color: $r('sys.color.ohos_id_color_focused_outline')
293            })
294            .borderRadius(MARGIN_NUM)
295        }
296      }
297    }
298    .onAreaChange((oldValue: Area, newValue: Area) => {
299      this.textArrowWidth = Number(parseInt(newValue.width.toString(), 0))
300    })
301    .constraintSize({ minWidth: this.flexWidth / DIVIDEND_WIDTH })
302    .justifyContent(FlexAlign.End)
303    .margin({ left: SPACE_MARGIN })
304  }
305
306  @Builder ButtonStyle(button: OperationOption) {
307    Row() {
308      Stack() {
309        Row() {
310          if (button != null) {
311            Text(button.value)
312              .maxLines(1)
313              .fontColor($r('sys.color.ohos_id_color_text_primary_activated'))
314              .fontSize($r('sys.float.ohos_id_text_size_button2'))
315              .fontWeight(FontWeight.Medium)
316              .margin({ left: SPACE_MARGIN, right: SPACE_MARGIN })
317              .focusable(true)
318          }
319        }
320        .justifyContent(FlexAlign.End)
321        .alignItems(VerticalAlign.Center)
322        .focusable(true)
323        .height(BUTTON_HEIGHT)
324        .margin({ left: SPACE_MARGIN, right: SPACE_MARGIN })
325        .borderRadius(IMAGE_WIDTH_NUM)
326        .backgroundColor(this.buttonBgColor)
327        .onFocus(() => {
328          this.buttonFocus = true
329        })
330        .onBlur(() => {
331          this.buttonFocus = false
332        })
333        .onTouch((event) => {
334          if (event.type === TouchType.Down) {
335            if (button.action) {
336              button.action()
337            }
338            this.buttonBgColor = $r('sys.color.ohos_id_color_click_effect')
339          }
340          if (event.type === TouchType.Up) {
341            this.buttonBgColor = $r('sys.color.ohos_id_color_sub_background_transparent')
342          }
343        })
344        .onHover((isHover: boolean) => {
345          if (isHover) {
346            this.buttonBgColor = $r('sys.color.ohos_id_color_hover')
347          } else {
348            this.buttonBgColor = $r('sys.color.ohos_id_color_sub_background_transparent')
349          }
350        })
351        .onKeyEvent((event) => {
352          if (event.keyCode === KeyCode.KEYCODE_ENTER || event.keyCode === KeyCode.KEYCODE_SPACE) {
353            button.action && button.action()
354          }
355        })
356        .onAreaChange((oldValue: Area, newValue: Area) => {
357          let flexWidth = Number(parseInt(newValue.width.toString(), 0))
358          this.buttonWidth = flexWidth
359        })
360
361        if (this.buttonFocus) {
362          Row()
363            .height(BUTTON_HEIGHT)
364            .width(this.buttonWidth)
365            .hitTestBehavior(HitTestMode.None)
366            .border({
367              width: BORDER_WIDTH,
368              color: $r('sys.color.ohos_id_color_focused_outline')
369            })
370            .borderRadius(IMAGE_WIDTH_NUM)
371        }
372      }
373    }
374    .constraintSize({ minWidth: this.flexWidth / DIVIDEND_WIDTH })
375    .justifyContent(FlexAlign.End)
376    .focusable(true)
377    .margin({ left: SPACE_MARGIN })
378  }
379
380  build() {
381    Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.End }) {
382      if (this.secondaryTitle != null && this.icon != null) {
383        Row() {
384          this.ListIconStyle({ content: this.secondaryTitle }, this.icon)
385        }.margin({ right: SPACE_MARGIN })
386      } else if (this.secondaryTitle != null && this.primaryTitle != null) {
387        this.SubTextStyle({ content: this.primaryTitle, subContent: this.secondaryTitle })
388      } else if (this.secondaryTitle != null) {
389        this.ListTextStyle({ content: this.secondaryTitle })
390      } else if (this.select != null) {
391        this.SelectStyle(this.select)
392      } else if (this.primaryTitle != null) {
393        this.ContentTextStyle({ content: this.primaryTitle })
394      }
395
396      Row() {
397        if (this.operationType === OperationType.BUTTON && this.operationItem != null) {
398          this.ButtonStyle(this.operationItem[0])
399        }
400        if (this.operationType === OperationType.ICON_GROUP && this.operationItem != null) {
401          Row() {
402            ForEach(this.operationItem, (item, index?: number) => {
403              if (index == 0) {
404                IconGroup({ item: item })
405              }
406              if (index == 1) {
407                IconGroup({ item: item })
408              }
409              if (index == 2) { // Image count
410                IconGroup({ item: item })
411              }
412            })
413          }
414        }
415        if (this.operationType === OperationType.TEXT_ARROW && this.operationItem != null) {
416          this.TextArrowStyle(this.operationItem[0])
417        }
418        if (this.operationType === OperationType.LOADING) {
419          this.LoadingProcessStyle()
420        }
421      }
422    }
423    .focusable(true)
424    .onAreaChange((oldValue: Area, newValue: Area) => {
425      let flexWidth = Number(parseInt(newValue.width.toString(), 0))
426      this.flexWidth = flexWidth - CONSTRAINT_NUM
427    })
428    .padding({ right: $r('sys.float.ohos_id_default_padding_end') })
429    .height(this.isDuplicateLine ? DOUBLE_LINE_HEIGHT : SINGLE_LINE_HEIGHT)
430  }
431}