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 16 17import { BaseElement, element } from '../../base-ui/BaseElement.js'; 18 19@element('sp-flags') 20export class SpFlags extends BaseElement { 21 private bodyEl: HTMLElement | undefined | null; 22 23 initElements(): void { 24 let parentElement = this.parentNode as HTMLElement; 25 parentElement.style.overflow = 'hidden'; 26 this.bodyEl = this.shadowRoot?.querySelector('.body'); 27 this.initConfigList(); 28 } 29 30 initHtml(): string { 31 return ` 32 ${this.initHtmlStyle()} 33 <div class="sp-flags-container"> 34 <div class="body"> 35 <h3 class="title">Feature flags</h3> 36 </div> 37 </div> 38 `; 39 } 40 41 private initHtmlStyle(): string { 42 return ` 43 <style> 44 .sp-flags-container { 45 background-color: var(--dark-background5,#F6F6F6); 46 min-height: 100%; 47 display: grid; 48 grid-template-columns: 1fr; 49 grid-template-rows:1fr; 50 } 51 :host{ 52 width: 100%; 53 height: 100%; 54 background-color: var(--dark-background5,#F6F6F6); 55 display: block; 56 } 57 .body{ 58 width: 85%; 59 margin: 2% 5% 2% 5%; 60 background-color: var(--dark-background3,#FFFFFF); 61 border-radius: 16px 16px 16px 16px; 62 padding-left: 2%; 63 padding-right: 4%; 64 } 65 .title { 66 padding-left: 2%; 67 margin-left: 8%; 68 } 69 .flag-widget { 70 width: 80%; 71 padding: 1% 2% 1% 2%; 72 margin-left: 8%; 73 margin-right: 8%; 74 border-radius: 10px 10px 10px 10px; 75 } 76 .flag-widget:nth-child(2n+1) { 77 background-color: #F5F5F5; 78 } 79 .flag-title-label { 80 margin-right: 10px; 81 flex-grow: 1; 82 text-align: left; 83 opacity: 0.9; 84 font-family: Helvetica-Bold; 85 font-size: 16px; 86 color: #000000; 87 line-height: 28px; 88 font-weight: 700; 89 } 90 .flag-head-div { 91 display: flex; 92 align-items: center; 93 } 94 .flag-des-div { 95 opacity: 0.6; 96 font-family: Helvetica; 97 font-size: 12px; 98 color: var(--dark-color,#000000); 99 text-align: left; 100 line-height: 20px; 101 font-weight: 400; 102 margin-top: 0.1%; 103 } 104 .config_footer { 105 margin-top: 1%; 106 } 107 .flag-select { 108 width: 12rem; 109 border: 1px solid var(--dark-color1,#4D4D4D); 110 border-radius: 16px; 111 opacity: 0.6; 112 font-family: Helvetica; 113 font-size: 12px; 114 color: var(--dark-color1,#000000); 115 text-align: center; 116 line-height: 20px; 117 font-weight: 400; 118 -webkit-appearance: none; 119 background: url(img/down.png) no-repeat 96% center; 120 } 121 .device_label { 122 font-weight: 500; 123 margin-right: 10px; 124 opacity: 0.9; 125 font-family: Helvetica-Bold; 126 font-size: 14px; 127 } 128 .device_input { 129 line-height: 20px; 130 font-weight: 400; 131 margin-right: 2%; 132 border-radius: 16px; 133 border: 1px solid #ccc; 134 padding-left: 10px; 135 } 136 </style> 137 `; 138 } 139 140 private createConfigDiv(): HTMLDivElement { 141 let configDiv = document.createElement('div'); 142 configDiv.className = 'flag-widget'; 143 return configDiv; 144 } 145 146 private createCustomDiv(config: FlagConfigItem, configDiv: HTMLDivElement): void { 147 let configHadDiv = document.createElement('div'); 148 configHadDiv.className = 'flag-head-div'; 149 let titleLabel = document.createElement('label'); 150 titleLabel.textContent = config.title; 151 titleLabel.className = 'flag-title-label'; 152 let configSelect = document.createElement('select'); 153 configSelect.className = 'flag-select'; 154 configSelect.setAttribute('title', config.title); 155 config.switchOptions.forEach((optionItem) => { 156 let configOption = document.createElement('option'); 157 configOption.value = optionItem.option; 158 configOption.textContent = optionItem.option; 159 if (optionItem.selected) { 160 configOption.selected = true; 161 } 162 configSelect.appendChild(configOption); 163 }); 164 configSelect.addEventListener('change', () => { 165 let title = configSelect.getAttribute('title'); 166 FlagsConfig.updateFlagsConfig(title!, configSelect.selectedOptions[0].value); 167 }); 168 let description = document.createElement('div'); 169 description.className = 'flag-des-div'; 170 description.textContent = config.describeContent; 171 configHadDiv.appendChild(titleLabel); 172 configHadDiv.appendChild(configSelect); 173 configDiv.appendChild(configHadDiv); 174 configDiv.appendChild(description); 175 } 176 177 private initConfigList(): void { 178 let allConfig = FlagsConfig.getAllFlagConfig(); 179 allConfig.forEach((config) => { 180 let configDiv = this.createConfigDiv(); 181 this.createCustomDiv(config, configDiv); 182 if (config.title === 'AnimationAnalysis') { 183 let configFooterDiv = document.createElement('div'); 184 configFooterDiv.className = 'config_footer'; 185 let deviceWidthLabelEl = document.createElement('label'); 186 deviceWidthLabelEl.className = 'device_label'; 187 deviceWidthLabelEl.textContent = 'PhysicalWidth :'; 188 let deviceWidthEl = document.createElement('input'); 189 deviceWidthEl.value = <string> config.addInfo!.physicalWidth; 190 deviceWidthEl.addEventListener('keyup', () => { 191 deviceWidthEl.value = deviceWidthEl.value.replace(/\D/g, ''); 192 }); 193 deviceWidthEl.addEventListener('blur', () => { 194 if (deviceWidthEl.value !== '') { 195 FlagsConfig.updateFlagsConfig('physicalWidth', Number(deviceWidthEl.value)); 196 } 197 }); 198 deviceWidthEl.className = 'device_input'; 199 let deviceHeightLabelEl = document.createElement('label'); 200 deviceHeightLabelEl.textContent = 'PhysicalHeight :'; 201 deviceHeightLabelEl.className = 'device_label'; 202 let deviceHeightEl = document.createElement('input'); 203 deviceHeightEl.className = 'device_input'; 204 deviceHeightEl.value = <string> config.addInfo!.physicalHeight; 205 deviceHeightEl.addEventListener('keyup', () => { 206 deviceHeightEl.value = deviceHeightEl.value.replace(/\D/g, ''); 207 }); 208 deviceHeightEl.addEventListener('blur', () => { 209 if (deviceWidthEl.value !== '') { 210 FlagsConfig.updateFlagsConfig('physicalHeight', Number(deviceHeightEl.value)); 211 } 212 }); 213 configFooterDiv.appendChild(deviceWidthLabelEl); 214 configFooterDiv.appendChild(deviceWidthEl); 215 configFooterDiv.appendChild(deviceHeightLabelEl); 216 configFooterDiv.appendChild(deviceHeightEl); 217 configDiv.appendChild(configFooterDiv); 218 } 219 this.bodyEl!.appendChild(configDiv); 220 }); 221 } 222} 223 224export type Params = { 225 [key: string]: unknown; 226}; 227 228export class FlagsConfig { 229 static FLAGS_CONFIG_KEY = 'FlagsConfig'; 230 static DEFAULT_CONFIG: Array<FlagConfigItem> = [ 231 { 232 title: 'TaskPool', 233 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 234 describeContent: 'Analyze TaskPool templates', 235 }, 236 { 237 title: 'AnimationAnalysis', 238 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 239 describeContent: 'Analyze Animation effect templates', 240 addInfo: { physicalWidth: 0, physicalHeight: 0 } 241 }, 242 { 243 title: 'AppStartup', 244 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 245 describeContent: 'App Startup templates', 246 }, 247 { 248 title: 'SchedulingAnalysis', 249 switchOptions: [{ option: 'Enabled' }, { option: 'Disabled', selected: true }], 250 describeContent: 'Scheduling analysis templates', 251 }, 252 ]; 253 254 static getAllFlagConfig(): Array<FlagConfigItem> { 255 let flagsConfigStr = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY); 256 if (flagsConfigStr === null) { 257 let flagConfigObj: Params = {}; 258 FlagsConfig.DEFAULT_CONFIG.forEach((config) => { 259 let selectedOption = config.switchOptions.filter((option) => { 260 return option.selected; 261 }); 262 let value = config.switchOptions[0].option; 263 if (selectedOption[0] !== undefined) { 264 value = selectedOption[0].option; 265 } 266 flagConfigObj[config.title] = value; 267 if (config.addInfo) { 268 for (const [key, value] of Object.entries(config.addInfo)) { 269 flagConfigObj[key] = value; 270 } 271 } 272 }); 273 window.localStorage.setItem(FlagsConfig.FLAGS_CONFIG_KEY, JSON.stringify(flagConfigObj)); 274 return FlagsConfig.DEFAULT_CONFIG; 275 } else { 276 let flagsConfig = JSON.parse(flagsConfigStr); 277 FlagsConfig.DEFAULT_CONFIG.forEach((config) => { 278 let cfg = flagsConfig[config.title]; 279 if (cfg) { 280 config.switchOptions.forEach((option) => { 281 if (option.option === cfg) { 282 option.selected = true; 283 } else { 284 option.selected = false; 285 } 286 }); 287 } 288 if (config.addInfo) { 289 for (const [key, value] of Object.entries(config.addInfo)) { 290 let cfg = flagsConfig[key]; 291 if (cfg) { 292 config.addInfo[key] = cfg; 293 } 294 } 295 } 296 }); 297 } 298 return FlagsConfig.DEFAULT_CONFIG; 299 } 300 301 static getSpTraceStreamParseConfig(): string { 302 let parseConfig = {}; 303 FlagsConfig.getAllFlagConfig().forEach( 304 configItem => { 305 let selectedOption = configItem.switchOptions.filter((option) => { 306 return option.selected; 307 }); 308 // @ts-ignore 309 parseConfig[configItem.title] = selectedOption[0].option === 'Enabled' ? 1 : 0; 310 } 311 ); 312 return JSON.stringify({ 'config': parseConfig }); 313 } 314 315 static getFlagsConfig(flagName: string): Params | undefined { 316 let flagConfigObj: Params = {}; 317 let configItem = FlagsConfig.getAllFlagConfig().find((config) => { 318 return config.title === flagName; 319 }); 320 if (configItem) { 321 let selectedOption = configItem.switchOptions.filter((option) => { 322 return option.selected; 323 }); 324 let value = configItem.switchOptions[0].option; 325 if (selectedOption[0] !== undefined) { 326 value = selectedOption[0].option; 327 } 328 flagConfigObj[configItem.title] = value; 329 if (configItem.addInfo) { 330 for (const [key, value] of Object.entries(configItem.addInfo)) { 331 flagConfigObj[key] = value; 332 } 333 } 334 return flagConfigObj; 335 } else { 336 return configItem; 337 } 338 } 339 340 static getFlagsConfigEnableStatus(flagName: string): boolean { 341 let config = FlagsConfig.getFlagsConfig(flagName); 342 let enable: boolean = false; 343 if (config && config[flagName]) { 344 enable = config[flagName] === 'Enabled'; 345 } 346 return enable; 347 } 348 349 static updateFlagsConfig(key: string, value: unknown): void { 350 let flagsConfigStr = window.localStorage.getItem(FlagsConfig.FLAGS_CONFIG_KEY); 351 let flagConfigObj: Params = {}; 352 if (flagsConfigStr !== null) { 353 flagConfigObj = JSON.parse(flagsConfigStr); 354 } 355 flagConfigObj[key] = value; 356 window.localStorage.setItem(FlagsConfig.FLAGS_CONFIG_KEY, JSON.stringify(flagConfigObj)); 357 } 358} 359 360export interface FlagConfigItem { 361 title: string; 362 switchOptions: OptionItem[]; 363 describeContent: string; 364 addInfo?: Params; 365} 366 367export interface OptionItem { 368 option: string; 369 selected?: boolean; 370} 371