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