1/* 2 * Copyright (c) 2022-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 router from '@ohos.router' 17import curves from '@ohos.curves' 18import { BreakpointSystem, BreakPointType } from '../common/BreakpointSystem' 19import { FoodInfo, Category } from '../model/DataModels' 20import { getFoods, getFoodCategories, getSortedFoodData } from '../model/DataUtil' 21import { Records } from './components/DietRecord' 22 23@Component 24struct FoodListItem { 25 private foodItem: FoodInfo 26 27 build() { 28 Navigator({ target: 'pages/FoodDetail' }) { 29 Row() { 30 Image(this.foodItem.image) 31 .objectFit(ImageFit.Contain) 32 .autoResize(false) 33 .height(40) 34 .width(40) 35 .backgroundColor('#FFf1f3f5') 36 .margin({ right: 16 }) 37 .borderRadius(6) 38 .sharedTransition(this.foodItem.letter, { 39 duration: 400, 40 curve: curves.cubicBezier(0.2, 0.2, 0.1, 1.0), 41 delay: 100 42 }) 43 Text(this.foodItem.name) 44 .fontSize(14) 45 Blank() 46 Text($r('app.string.calorie_with_kcal_unit', this.foodItem.calories.toString())) 47 .fontSize(14) 48 } 49 .height(64) 50 .width('100%') 51 } 52 .params({ foodId: this.foodItem }) 53 .margin({ right: 24, left: 32 }) 54 } 55} 56 57@Component 58struct ListModeFoods { 59 private foodItems: FoodInfo[] = getSortedFoodData() 60 61 build() { 62 Column() { 63 Text($r("app.string.title_food_list")) 64 .width('100%') 65 .id('foodList') 66 .height(56) 67 .padding({ left: 20 }) 68 .backgroundColor('#FF1f3f5') 69 .fontSize(20) 70 71 List() { 72 ForEach(this.foodItems, (item) => { 73 ListItem() { 74 if (item.letter !== undefined) { 75 FoodListItem({ foodItem: item }) 76 } else { 77 Text(item) 78 .fontSize(14) 79 .height(48) 80 .margin({ left: 24 }) 81 .width('100%') 82 } 83 } 84 }) 85 } 86 .layoutWeight(1) 87 } 88 } 89} 90 91@Component 92struct FoodGridItem { 93 private foodItem: FoodInfo 94 private foodItemIndex: number; 95 96 build() { 97 Column() { 98 Image(this.foodItem.image) 99 .objectFit(ImageFit.Contain) 100 .height(152) 101 .sharedTransition(this.foodItem.letter, { 102 duration: 400, 103 curve: curves.cubicBezier(0.2, 0.2, 0.1, 1.0), 104 delay: 100 105 }) 106 Row() { 107 Text(this.foodItem.name) 108 .fontSize(14) 109 Blank() 110 Text($r('app.string.calorie_with_kcal_unit', this.foodItem.calories.toString())) 111 .fontSize(14) 112 .fontColor(0x99000000) 113 } 114 .padding({ left: 12, right: 12 }) 115 .width('100%') 116 .height(32) 117 .backgroundColor('#E5E5E5') 118 } 119 .id(`foodItem${this.foodItemIndex}`) 120 .height(184) 121 .backgroundColor('#f1f3f5') 122 .clip(new Rect({ width: '100%', height: '100%', radius: 12 })) 123 .onClick(() => { 124 router.push({ url: 'pages/FoodDetail', params: { foodId: this.foodItem } }) 125 }) 126 } 127} 128 129@Component 130struct FoodGrid { 131 @StorageProp('currentBreakpoint') currentBreakpoint: string = 'md' 132 private foodItems: FoodInfo[] 133 134 build() { 135 Grid() { 136 ForEach(this.foodItems, (item: FoodInfo, index: number) => { 137 GridItem() { 138 FoodGridItem({ foodItem: item, foodItemIndex: index }) 139 } 140 }) 141 } 142 .columnsTemplate(new BreakPointType({ 143 sm: '1fr 1fr', 144 md: '1fr 1fr 1fr', 145 lg: '1fr 1fr 1fr 1fr' 146 }).getValue(this.currentBreakpoint)) 147 .columnsGap(8) 148 .rowsGap(8) 149 .padding({ left: 16, right: 16 }) 150 } 151} 152 153@Component 154struct CategoryModeFoods { 155 @State currentTabIndex: number = 0 156 private foodItems: FoodInfo[] = getFoods() 157 private foodCategories: Category[] = getFoodCategories() 158 159 @Builder tabBarItemBuilder(value: Resource, index: number) { 160 Text(value) 161 .fontColor(this.currentTabIndex === index ? 'rgba(0,0,0,0.9)' : 'rgba(0,0,0,0.6)') 162 .fontSize(this.currentTabIndex === index ? 24 : 18) 163 .margin({ top: 2, left: 10, right: 10 }) 164 .height(56) 165 } 166 167 build() { 168 Tabs() { 169 TabContent() { 170 FoodGrid({ foodItems: this.foodItems }) 171 }.tabBar(this.tabBarItemBuilder($r('app.string.category_all'), 0)) 172 173 ForEach(this.foodCategories, (foodCategory: Category, index) => { 174 TabContent() { 175 FoodGrid({ foodItems: this.foodItems.filter(item => (item.categoryId === foodCategory.id)) }) 176 }.tabBar(this.tabBarItemBuilder(foodCategory.name, index + 1)) 177 }) 178 } 179 .animationDuration(0) 180 .barMode(BarMode.Scrollable) 181 .onChange((index) => { 182 this.currentTabIndex = index 183 }) 184 } 185} 186 187@Component 188struct FoodsDisplay { 189 @State isCategoryMode: boolean = true 190 191 build() { 192 Stack({ alignContent: Alignment.TopEnd }) { 193 if (this.isCategoryMode) { 194 CategoryModeFoods() 195 } else { 196 ListModeFoods() 197 } 198 Row() { 199 Image($r("app.media.ic_switch")) 200 .height(24) 201 .width(24) 202 .id('switch') 203 .margin({ left: 24, right: 24 }) 204 } 205 .height(56) 206 .backgroundColor(this.isCategoryMode ? Color.White : '#F1F3F5') 207 .onClick(() => { 208 this.isCategoryMode = !this.isCategoryMode 209 }) 210 } 211 } 212} 213 214@Entry 215@Component 216struct Home { 217 @State currentTabIndex: number = 0 218 @StorageProp('currentBreakpoint') currentBreakpoint: string = 'md' 219 private breakpointSystem: BreakpointSystem = new BreakpointSystem() 220 221 @Builder bottomBarItemBuilder(name: Resource, icon: Resource, index: number) { 222 Flex({ 223 direction: new BreakPointType({ 224 sm: FlexDirection.Column, 225 md: FlexDirection.Row, 226 lg: FlexDirection.Column 227 }).getValue(this.currentBreakpoint), 228 justifyContent: FlexAlign.Center, 229 alignItems: ItemAlign.Center 230 }) { 231 Image(icon) 232 .height(24) 233 .width(24) 234 .fillColor(this.getTabBarColor(index)) 235 Text(name) 236 .margin(new BreakPointType({ 237 sm: { top: 4 }, 238 md: { left: 8 }, 239 lg: { top: 4 } 240 }).getValue(this.currentBreakpoint)) 241 .fontSize(11) 242 .fontColor(this.getTabBarColor(index)) 243 } 244 .width('100%') 245 .height('100%') 246 .id(`tabTitle${index}`) 247 } 248 249 aboutToAppear() { 250 this.breakpointSystem.register() 251 } 252 253 aboutToDisappear() { 254 this.breakpointSystem.unregister() 255 } 256 257 build() { 258 Tabs({ 259 barPosition: new BreakPointType({ 260 sm: BarPosition.End, 261 md: BarPosition.End, 262 lg: BarPosition.Start 263 }).getValue(this.currentBreakpoint) 264 }) { 265 TabContent() { 266 FoodsDisplay() 267 }.tabBar(this.bottomBarItemBuilder($r("app.string.tab_bar_home"), $r("app.media.ic_bottom_home"), 0)) 268 269 TabContent() { 270 Records() 271 }.tabBar(this.bottomBarItemBuilder($r("app.string.tab_bar_record"), $r("app.media.ic_bottom_record"), 1)) 272 } 273 .vertical(new BreakPointType({ sm: false, md: false, lg: true }).getValue(this.currentBreakpoint)) 274 .barWidth(new BreakPointType({ sm: '100%', md: '100%', lg: '56vp' }).getValue(this.currentBreakpoint)) 275 .barHeight(new BreakPointType({ sm: '56vp', md: '56vp', lg: '60%' }).getValue(this.currentBreakpoint)) 276 .animationDuration(0) 277 .onChange((index) => { 278 this.currentTabIndex = index 279 }) 280 } 281 282 private getTabBarColor(index: number) { 283 return this.currentTabIndex == index ? $r('app.color.tab_bar_select_color') : $r('app.color.tab_bar_normal_color') 284 } 285}