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 .stateStyles({ 285 focused: { 286 .border({ 287 radius: MARGIN_NUM, 288 width: BORDER_WIDTH, 289 color: $r('sys.color.ohos_id_color_focused_outline'), 290 style: BorderStyle.Solid 291 }) 292 }, 293 normal: { 294 .border({ 295 radius: MARGIN_NUM, 296 width: BORDER_WIDTH, 297 color: Color.Transparent 298 }) 299 } 300 }) 301 } 302 } 303 .onAreaChange((oldValue: Area, newValue: Area) => { 304 this.textArrowWidth = Number(parseInt(newValue.width.toString(), 0)) 305 }) 306 .constraintSize({ minWidth: this.flexWidth / DIVIDEND_WIDTH }) 307 .justifyContent(FlexAlign.End) 308 .margin({ left: SPACE_MARGIN }) 309 } 310 311 @Builder ButtonStyle(button: OperationOption) { 312 Row() { 313 Stack() { 314 Row() { 315 if (button != null) { 316 Text(button.value) 317 .maxLines(1) 318 .fontColor($r('sys.color.ohos_id_color_text_primary_activated')) 319 .fontSize($r('sys.float.ohos_id_text_size_button2')) 320 .fontWeight(FontWeight.Medium) 321 .margin({ left: SPACE_MARGIN, right: SPACE_MARGIN }) 322 .focusable(true) 323 } 324 } 325 .justifyContent(FlexAlign.End) 326 .alignItems(VerticalAlign.Center) 327 .focusable(true) 328 .height(BUTTON_HEIGHT) 329 .margin({ left: SPACE_MARGIN, right: SPACE_MARGIN }) 330 .borderRadius(IMAGE_WIDTH_NUM) 331 .backgroundColor(this.buttonBgColor) 332 .onFocus(() => { 333 this.buttonFocus = true 334 }) 335 .onBlur(() => { 336 this.buttonFocus = false 337 }) 338 .onTouch((event) => { 339 if (event.type === TouchType.Down) { 340 if (button.action) { 341 button.action() 342 } 343 this.buttonBgColor = $r('sys.color.ohos_id_color_click_effect') 344 } 345 if (event.type === TouchType.Up) { 346 this.buttonBgColor = $r('sys.color.ohos_id_color_sub_background_transparent') 347 } 348 }) 349 .onHover((isHover: boolean) => { 350 if (isHover) { 351 this.buttonBgColor = $r('sys.color.ohos_id_color_hover') 352 } else { 353 this.buttonBgColor = $r('sys.color.ohos_id_color_sub_background_transparent') 354 } 355 }) 356 .onKeyEvent((event) => { 357 if (event.keyCode === KeyCode.KEYCODE_ENTER || event.keyCode === KeyCode.KEYCODE_SPACE) { 358 button.action && button.action() 359 } 360 }) 361 .onAreaChange((oldValue: Area, newValue: Area) => { 362 let flexWidth = Number(parseInt(newValue.width.toString(), 0)) 363 this.buttonWidth = flexWidth 364 }) 365 366 if (this.buttonFocus) { 367 Row() 368 .height(BUTTON_HEIGHT) 369 .width(this.buttonWidth) 370 .hitTestBehavior(HitTestMode.None) 371 .border({ 372 width: BORDER_WIDTH, 373 color: $r('sys.color.ohos_id_color_focused_outline') 374 }) 375 .borderRadius(IMAGE_WIDTH_NUM) 376 } 377 } 378 } 379 .constraintSize({ minWidth: this.flexWidth / DIVIDEND_WIDTH }) 380 .justifyContent(FlexAlign.End) 381 .focusable(true) 382 .margin({ left: SPACE_MARGIN }) 383 } 384 385 build() { 386 Flex({ justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.End }) { 387 if (this.secondaryTitle != null && this.icon != null) { 388 Row() { 389 this.ListIconStyle({ content: this.secondaryTitle }, this.icon) 390 }.margin({ right: SPACE_MARGIN }) 391 } else if (this.secondaryTitle != null && this.primaryTitle != null) { 392 this.SubTextStyle({ content: this.primaryTitle, subContent: this.secondaryTitle }) 393 } else if (this.secondaryTitle != null) { 394 this.ListTextStyle({ content: this.secondaryTitle }) 395 } else if (this.select != null) { 396 this.SelectStyle(this.select) 397 } else if (this.primaryTitle != null) { 398 this.ContentTextStyle({ content: this.primaryTitle }) 399 } 400 401 Row() { 402 if (this.operationType === OperationType.BUTTON && this.operationItem != null) { 403 this.ButtonStyle(this.operationItem[0]) 404 } 405 if (this.operationType === OperationType.ICON_GROUP && this.operationItem != null) { 406 Row() { 407 ForEach(this.operationItem, (item, index?: number) => { 408 if (index == 0) { 409 IconGroup({ item: item }) 410 } 411 if (index == 1) { 412 IconGroup({ item: item }) 413 } 414 if (index == 2) { // Image count 415 IconGroup({ item: item }) 416 } 417 }) 418 } 419 } 420 if (this.operationType === OperationType.TEXT_ARROW && this.operationItem != null) { 421 this.TextArrowStyle(this.operationItem[0]) 422 } 423 if (this.operationType === OperationType.LOADING) { 424 this.LoadingProcessStyle() 425 } 426 } 427 } 428 .focusable(true) 429 .onAreaChange((oldValue: Area, newValue: Area) => { 430 let flexWidth = Number(parseInt(newValue.width.toString(), 0)) 431 this.flexWidth = flexWidth - CONSTRAINT_NUM 432 }) 433 .padding({ right: $r('sys.float.ohos_id_default_padding_end') }) 434 .height(this.isDuplicateLine ? DOUBLE_LINE_HEIGHT : SINGLE_LINE_HEIGHT) 435 } 436}