w;%%XdT2T}a$X4yR7EI;zaof3a)5Z;`OwVi%D?gbkBj!{;z2tOBSFk&E1De iZXD**uvNqL}+|pO{ ztO$}2NMRit2ddU?)7Prq&*&H3X>&=E{-+j4i Uz zrvL;?0$^@lyl=LHz9G^$SJV6ID__@7z->Bh>Vm=6AK&5bP%@heveHja5F@agGgUsY z@L@W2+^*NVoId0!kS~4XkWb%y;f}XBf>S+NIw9aHK;vN+4mJ|em)_QjIVfb2$;bwv zDKmoq6AThgKydS6Hs+UpKPWq|UA}s=UOEBZNM3oNT5qTAabY)X>L6jxfGDuu7&GD_ z=@@m?sJ-o2GS}&hNRW}-zHkr>o4&138@a8IC-FjSBxzjx?(*3@YmdmWGAd%0QvXzS zJ53JpX%Fp!=>v&`Hd7F@+Atw2vx9%^2M-APg0Jd|ePsRn3*B$#9Z5hCou4fo7W#SN z#}-@-N=##yQDh26pNzr9f*Q88krhI5@DHcf{dU-~PLSs}MvI4s1i|<=qxD~9`7>*~ znlw5lr$_6mTG4XbBNF_79BzvZ!TeIP)exdk3)kSHjYdW1P10ZJ_NCJSlrCuIU#gqw f88(SSw!Z%ZUzhC#9QlKF00000NkvXXu0mjfG$}gK literal 0 HcmV?d00001 diff --git a/code/ArkTS1.2/ComponentSample/README.md b/code/ArkTS1.2/ComponentSample/README.md new file mode 100644 index 0000000000..ba7b57f0ec --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/README.md @@ -0,0 +1,102 @@ +# 组件复用示例 + +### 介绍 + +1.主页实现图文混排组件复用场景; +2.实现日历场景的组件复用,属于高负载刷新的组件复用场景; + +### 效果预览 + +效果如下所示: + +|主界面|列表二级联动|自定义日历选择器|跨文件样式复用和组件复用|合理处理高负载组件的渲染文章示例代码| +|--------------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------| +|||||| + +使用说明 + +1. 在主界面,点击蓝色按钮"列表二级联动"。 + * 滑动二级列表侧控件,一级列表随之滚动。 + * 点击一级列表,二级列表随之滚动。 +2. 在主界面,点击蓝色按钮"自定义日历选择器"。 + * 点击购物车页面的list列表跳转商品详情页。 +3. 在主界面,点击蓝色按钮"跨文件样式复用和组件复用"。 + * 加载完成后显示主界面,点当前日期后会显示日历选择器,选择日期后会关闭日历选择器,主页面日期会变成选定的日期。 +4. 在主界面,点击蓝色按钮"合理处理高负载组件的渲染文章示例代码"。 + * 加载10年的日历数据。 + + +### 工程目录 + +``` +entry/src/main/ets/ +|---pages +| |---Example1 +| | |---DataType.ets // 数据类型定义 +| | |---Example1.ets +| | |---SecondaryLinkExample.ets // 二级联动功能实现页面 +| |---Example2 +| | |---components +| | | |---DateModel.ets // 数据类型定义 +| | | |---GetDate.ets // 获取日期信息 +| | | |---MonthDataSource.ets // 数据类型定义 +| | |----view +| | | |---CalendarView.ets // 场景主页面 + 自定义日历 +| | |---Example2.ets +| |---Example3 +| | |---components +| | | |---CommonText.ets // 自定义组件封装 +| | | |---LazyForEach.ets // 懒加载 +| | |----view +| | | |---Details.ets // 页面:详情页 +| | | |---ShoppingCart.ets // 页面:购物车 +| | |---Example3.ets +| |---Example4 +| | |---GetDate.ets // 获取日期信息 +| | |---MonthDataSource.ets // 懒加载数据类型 +| | |---Example4.ets +| | |---ReusePage.ets // 正常加载数据的页面 +| |---index.ets // 首页 +``` + +### 具体实现 + +* Example1(列表二级联动) 源码参考: [Example1](entry/src/main/ets/pages/Example1) + * 构造懒加载数据源类型[MyDataSource](entry/src/main/ets/pages/Example1/DataType.ets) + * 一二级列表分别绑定不同的Scroller对象,一级列表(tagLists)绑定classifyScroller对象,二级列表绑定scroller对象。 + * 通过循环,构造一二级列表数据。 + * 点击一级列表后,通过一级列表的索引获取二级列表的索引,调用scrollToIndex方法将一二级列表滚动到指定索引值。 + * 滑动二级列表触发组件滚动事件后,获取到列表可视区域第一个item对应的索引值,通过二级列表索引获取一级列表索引,调用scrollToIndex方法将一级列表滚动到指定索引值。 +* Example2(自定义日历选择器) 源码参考: [Example2](entry/src/main/ets/pages/Example2) + * 获取当前月和下个月的日期信息。源码参考[GetDate.ets](entry/src/main/ets/pages/Example2/components/GetDate.ets) + * 通过Flex类初始化自定义日历界面。源码参考[CalendarView.ets](entry/src/main/ets/pages/Example2/view/CalendarView.ets)。 +* Example3(跨文件样式复用和组件复用) 源码参考: [Example3](entry/src/main/ets/pages/Example3) + * 使用了自定义封装的Image+Text的图文复合组件[ImageText](entry/src/main/ets/pages/Example3/common/CommonText.ets) +* Example4(合理处理高负载组件的渲染文章示例代码) 源码参考: [Example4](entry/src/main/ets/pages/Example4) + * 通过组件复用,加载10年的日历数据。源码参考:[ReusePage.ets](entry/src/main/ets/pages/Example4/ReusePage.ets) + +### 相关权限 + +无 + +### 依赖 + +无 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:Phone; +2. 本示例为Stage模型,支持API20版本SDK,SDK版本号(API Version 20),镜像版本号(5.0.0.5)。 +3. 本示例需要使用DevEco Studio 版本号(6.0.0.6)版本才可编译运行。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo code/ArkTS1.2/ComponentSample/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/openharmony/applications_app_samples.git +git pull +``` \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/build-profile.json5 b/code/ArkTS1.2/ComponentSample/build-profile.json5 new file mode 100644 index 0000000000..c83260a8d4 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/build-profile.json5 @@ -0,0 +1,68 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "arkTSVersion": "1.2", + "compatibleSdkVersion": "6.0.0(20)", + "runtimeOS": "HarmonyOS", + "buildOption": { + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + } + } + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/code-linter.json5 b/code/ArkTS1.2/ComponentSample/code-linter.json5 new file mode 100644 index 0000000000..87b3919d41 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/code-linter.json5 @@ -0,0 +1,58 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +{ + "files": [ + "**/*.ets" + ], + "ignore": [ + "**/src/ohosTest/**/*", + "**/src/test/**/*", + "**/src/mock/**/*", + "**/node_modules/**/*", + "**/oh_modules/**/*", + "**/build/**/*", + "**/.preview/**/*" + ], + "ruleSet": [ + "plugin:@performance/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "@security/no-unsafe-aes": "error", + "@security/no-unsafe-hash": "error", + "@security/no-unsafe-mac": "warn", + "@security/no-unsafe-dh": "error", + "@security/no-unsafe-dsa": "error", + "@security/no-unsafe-ecdsa": "error", + "@security/no-unsafe-rsa-encrypt": "error", + "@security/no-unsafe-rsa-sign": "error", + "@security/no-unsafe-rsa-key": "error", + "@security/no-unsafe-dsa-key": "error", + "@security/no-unsafe-dh-key": "error", + "@security/no-unsafe-3des": "error" + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/.gitignore b/code/ArkTS1.2/ComponentSample/entry/.gitignore new file mode 100644 index 0000000000..e2713a2779 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/build-profile.json5 b/code/ArkTS1.2/ComponentSample/entry/build-profile.json5 new file mode 100644 index 0000000000..36f9e65afb --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/build-profile.json5 @@ -0,0 +1,54 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/hvigorfile.ts b/code/ArkTS1.2/ComponentSample/entry/hvigorfile.ts new file mode 100644 index 0000000000..8f830cdfd5 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/hvigorfile.ts @@ -0,0 +1,32 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/code/ArkTS1.2/ComponentSample/entry/obfuscation-rules.txt b/code/ArkTS1.2/ComponentSample/entry/obfuscation-rules.txt new file mode 100644 index 0000000000..272efb6ca3 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/oh-package.json5 b/code/ArkTS1.2/ComponentSample/entry/oh-package.json5 new file mode 100644 index 0000000000..39ab3f9014 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/oh-package.json5 @@ -0,0 +1,36 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/entryability/EntryAbility.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000..7cfa743131 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import UIAbility from '@ohos.app.ability.UIAbility'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; +import Want from '@ohos.app.ability.Want'; +import window from '@ohos.window'; +import { BusinessError } from '@ohos.base'; +import hilog from '@ohos.hilog'; +import {globalContext} from './GlobalContext'; + +class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', 'EntryAbility onCreate'); + globalContext.setAbilityContext(this.context); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + hilog.info(0x0000, 'testTag', 'EntryAbility onWindowStageCreate'); + try { + windowStage.loadContent('pages/Index', (err: BusinessError ): void => { + hilog.info(0x0000, 'testTag', 'loadContent entering'); + if (err.code) { + hilog.error(0x0000, 'testTag', 'loadContent error'); + return; + } + hilog.info(0x0000, 'testTag', 'loadContent ok'); + }); + } catch (e: Error) { + hilog.error(0x0000, 'testTag', 'loadContent catch error:-----------' + e.message); + } + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/entryability/GlobalContext.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/entryability/GlobalContext.ets new file mode 100644 index 0000000000..e63a822c2f --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/entryability/GlobalContext.ets @@ -0,0 +1,44 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import common from '@ohos.app.ability.common'; + +export class GlobalContext { + private abilityContext: common.UIAbilityContext | null = null; + + setAbilityContext(context: common.UIAbilityContext): void { + this.abilityContext = context; + } + + getAbilityContext(): common.UIAbilityContext { + if (!this.abilityContext) { + throw new Error('AbilityContext'); + } + return this.abilityContext!; + } +} + +export const globalContext = new GlobalContext(); \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example1/DataType.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example1/DataType.ets new file mode 100644 index 0000000000..82d7e7734e --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example1/DataType.ets @@ -0,0 +1,206 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import {IDataSource,DataChangeListener,Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView ,Image,Row} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins +import { State, StateDecoratedVariable, MutableState, stateOf, observableProxy ,Provide} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +const CONTENT_PER_TAG = 10; // 每个TAG对应多少个元素 +/** + * 代表自定义类型数据的接口。 + * + * @interface + * @property {string} desc - 描述。 + * @property {string} tag - 类别。 + */ +export interface CustomDataType { + desc: string, + tag: string, +} + +/** + * 一级列表可视区域的起始索引和终点索引。 + * + * @interface + * @property {number} start - 可视区域起点索引。 + * @property {number} end - 可视区域终点索引。 + */ +export interface ListIndexPosition { + start: number, + end: number +} + +/** + * Basic implementation of IDataSource to handle data listener + * + * @class + * @implements {IDataSource} + */ +class BasicDataSource implements IDataSource { + private listeners: DataChangeListener[] = Array (); + private originDataArray: CustomDataType[] = Array (); + + /** + * 获取数组长度。 + * @returns {number} 返回数组长度。 + */ + public totalCount(): double { + return 0; + } + + /** + * 获取指定索引数据。 + * @param {number} index - 索引值。 + * @returns {CustomDataType} 返回指定索引数据。 + */ + public getData(index: double): CustomDataType { + return this.originDataArray[index]; + } + + /** + * 为LazyForEach组件向其数据源处添加listener监听。 + * @param {DataChangeListener} listener - 监听对象。 + */ + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + console.info('add listener'); + this.listeners.push(listener); + } + } + + /** + * 为对应的LazyForEach组件在数据源处去除listener监听。 + * @param {DataChangeListener} listener - 监听对象。 + */ + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + console.info('remove listener'); + this.listeners.splice(pos, 1); + } + } + + /** + * 通知LazyForEach组件需要在index对应索引处添加子组件。 + * @param {number} index - 索引值。 + */ + notifyDataAdd(index: double): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + }) + } + + /** + * 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件。 + * @param {number} index - 索引值。 + */ + notifyDataChange(index: double): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + }) + } + + /** + * 通知LazyForEach组件需要在index对应索引处删除该子组件 + * @param {number} index - 索引值。 + */ + notifyDataDelete(index: double): void { + this.listeners.forEach(listener => { + listener.onDataDelete(index); + }) + } + + /** + * 通知LazyForEach组件将from索引和to索引处的子组件进行交换 + * @param {number} from - 起始值。 + * @param {number} to - 终点值。 + */ + notifyDataMove(from: double, to: double): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + }) + } +} + +/** + * 继承自BasicDataSource的子类,重写了方法。 + * + * @class + * @extends {BasicDataSource} + */ +export class MyDataSource extends BasicDataSource { + public dataArray: CustomDataType[] = Array (); + + /** + * 获取数组长度。 + * @returns {number} 返回数组长度。 + */ + public totalCount(): double { + return this.dataArray.length; + } + + /** + * 获取指定索引数据。 + * @param {number} index - 索引值。 + * @returns {CustomDataType} 返回指定索引数据。 + */ + public getData(index: double): CustomDataType { + return this.dataArray[index]; + } + + /** + * 改变单个数据。 + * @param {number} index - 索引值。 + * @param {CustomDataType} data - 修改后的值。 + */ + public addData(index: double, data: CustomDataType): void { + this.dataArray.splice(index, 0, data); + this.notifyDataAdd(index); + } + + /** + * 添加数据。 + * @param {CustomDataType} data - 需要添加的数据。 + */ + public pushData(data: CustomDataType[]): void { + if (Array.isArray(data)) { + for (let i = 0; i < CONTENT_PER_TAG; ++i) { + this.dataArray.push(data[i]); + } + } + this.notifyDataAdd(this.dataArray.length - 1); + } +} + +// 常量数据 +export enum COMPONENT_STYLE { + ITEM_GUTTER = 12, + TAG_TEXT_HEIGHT= 75, + SUB_ITEM_GUTTER= 7, + SUB_ITEM_HEIGHT= 96, + SUB_ITEM_TEXT_WIDTH_TITLE= 56, + SUB_ITEM_TEXT_HEIGHT= 12, + SUB_ITEM_TEXT_WIDTH_BODY= 120, + BOTTOM_TOAST_TEXT_MAX_HEIGHT= 200 +}; \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example1/Example1.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example1/Example1.ets new file mode 100644 index 0000000000..1613fbcac3 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example1/Example1.ets @@ -0,0 +1,83 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { + memo, + __memo_context_type, + __memo_id_type +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + Row, + TextAlign, + Entry +} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins +import { + State, + Link, + StateDecoratedVariable, + MutableState, + stateOf, + observableProxy +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import hilog from '@ohos.hilog' +import { Context,UIContext} from '@ohos.arkui.UIContext' +import { SecondaryLinkExample } from './SecondaryLinkExample'; +@Entry +@Component +struct Example1Test { + aboutToAppear() { + + } + build() { + Column() { + Row() { + Text("← 返回").fontSize(20) + .onClick((e: ClickEvent) => { + this.getUIContext().getRouter().back(); + }).width("100%") + }.height("5%") + SecondaryLinkExample() + } + } +} + +export class ComExampleTrivialApplication extends UserView { + getBuilder() { + hilog.info(0x0000, 'testTag', 'getBuilder'); + let wrapper = @memo() =>{ + hilog.info(0x0000, 'testTag', 'Example1Test'); + Example1Test(undefined); + } + return wrapper; + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example1/SecondaryLinkExample.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example1/SecondaryLinkExample.ets new file mode 100644 index 0000000000..44d4854581 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example1/SecondaryLinkExample.ets @@ -0,0 +1,307 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { CustomDataType, MyDataSource, ListIndexPosition, COMPONENT_STYLE } from './DataType'; +import { + memo, + __memo_context_type, + __memo_id_type +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import { + WordBreak, + TextOverflow, + TouchType, + TextAlign, + FontWeight, + FlexAlign, + HorizontalAlign, + BarState, + Scroller, + Scroll, + SafeAreaType, + SafeAreaEdge, + ListItem, + Axis, + LazyForEach, + List, + TouchEvent, + ForEach, + Flex, + FlexWrap, + Builder, + NavDestination, + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + Image, + Row, + CalendarPickerDialog,$r +} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins +import { + State, + StateDecoratedVariable, + MutableState, + stateOf, + observableProxy, + Watch +} from "@ohos.arkui.stateManagement";// should be insert by ui-plugins +import hilog from '@ohos.hilog'; + +const TAG_LIST_LENGTH = 12; // TagList长度 +const CONTENT_PER_TAG = 10; // 每个TAG对应多少个元素 + +/** + * 功能描述: 本示例主要介绍了List组件实现二级联动(Cascading List)的场景 + * + * 推荐场景: 需要使用多级列表联合滚动的场景,如:外卖点单页面等 + * + * 核心组件: + * 1. SecondaryLinkExample.tagListItemBuilder + * + * 实现步骤: + * 1. 一二级列表分别绑定不同的Scroller对象,一级列表(tagLists)绑定classifyScroller对象,二级列表绑定scroller对象 + * 2. 点击一级列表后,通过一级列表的索引获取二级列表的索引,调用scrollToIndex方法将一二级列表滚动到指定索引值 + * 3. 滑动二级列表触发组件滚动事件后,获取到列表可视区域第一个item对应的索引值,通过二级列表索引获取一级列表索引,调用scrollToIndex方法将一级列表滚动到指定索引值 + */ +@Component +export struct SecondaryLinkExample { + private tagIndexPosition: ListIndexPosition = { start: 0, end: 0 } as ListIndexPosition; // 一级列表可视区域的起始索引和终点索引 初始化失败 + @State @Watch('onIndexChange') currentTagIndex: number = 0; // 一级列表焦点索引值 + private tagLists: Array = new Array (); // 一级列表数据 + @State contentData: MyDataSource = new MyDataSource(); // 二级列表数据 + private records: Array = new Array (); // 二级列表分组count数量 + // TODO:知识点: 1.级联列表分别绑定不同的Scroller对象,通过调用Scroller对象方法实现控制列表滚动 + private classifyScroller: Scroller = new Scroller(); // 一级列表Scroller对象 + private scroller: Scroller = new Scroller(); // 二级列表Scroller对象 + private isClickTagList: boolean = false; // 是否点击一级列表 + + /** + * 生命周期函数 + */ + aboutToAppear(): void { + // 构造数据 + for (let i = 0; i < TAG_LIST_LENGTH; i++) { + this.tagLists.push(`类别${i + 1}`); + const tempData: Array = new Array (CONTENT_PER_TAG).fill({ + desc: '内容数据', + tag: `类别${i + 1}` + }); + this.records.push(i * CONTENT_PER_TAG); + this.contentData.pushData(tempData); + } + this.records.push(CONTENT_PER_TAG * TAG_LIST_LENGTH); + this.tagIndexPosition = { start: 0, end: 0 }; + } + + build() { + Column() { + Column() { + Text($r('app.string.secondarylinkage_secondary_link')) + .fontSize(20) + Text($r('app.string.secondarylinkage_secondary_link_desc')) + .fontSize(20) + .wordBreak(WordBreak.BREAK_ALL) + } + .width('100%') + .backgroundColor("#ffcac6c6") + .borderRadius(12) + .padding(12) + + Row() { + // TODO:知识点: 2.一级列表绑定Scroller对象 + List({ scroller: this.classifyScroller, initialIndex: 0 }) { + ForEach(this.tagLists, (item: string, index: number) => { + this.tagListItemBuilder(item, index); + }, (item: string, index: number) => item) + } + .backgroundColor("#ffe3e3e3") + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]) + .onScrollIndex((start: number, end: number): void => { + this.tagIndexPosition = { start, end }; + }) + .listDirection(Axis.Vertical) + .scrollBar(BarState.Off) + .height('100%') + .width('30%') + // 二级列表 + List({ scroller: this.scroller}) { + ForEach(this.contentData.dataArray, (item: CustomDataType, index: number) => { + this.contentListItemBuilder(item, index); + }) + this.contentListNoMoreBuilder(); + } + .backgroundColor("#ffffffff") + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]) + .id('list_content') + .scrollBar(BarState.Off) + .listDirection(Axis.Vertical) // 列表排列方向水平 + .flexShrink(1) + .onTouch((event: TouchEvent): void => { + if (event.type === TouchType.Down) { + this.isClickTagList = false; // 当前点击的非一级列表 + } + }) + // 性能知识点:onScrollIndex事件在列表滚动时频繁执行,在回调中需要尽量减少耗时和冗余操作,例如减少不必要的日志打印 + .onScrollIndex((start: number): void => { + // TODO:知识点: 4.滑动二级列表触发组件滚动事件后,返回列表可视区域的第一个item对应的索引值,当前通过二级列表索引获取一级列表索引,操作一级列表Scroller对象使列表滚动到指定位置 + const currentClassIndex = this.findClassIndex(start); + if (currentClassIndex !== this.currentTagIndex && this.isClickTagList !== true) { + this.currentTagIndex = currentClassIndex; + this.classifyScroller.scrollToIndex(currentClassIndex, true); + } + }) + } + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]) + .width('100%') + .layoutWeight(1) + } + } + + /** + * 内容底部"没有更多"视图 + */ + @Builder + contentListNoMoreBuilder() { + ListItem() { + Text($r('app.string.secondarylinkage_nomore')) + .fontSize(20) + .backgroundColor("#ffb8b8b8") + } + .width('100%') + } + + /** + * 右侧内容视图 + */ + @Builder + contentListItemBuilder(item: CustomDataType, index: number) { + ListItem() { + Row() { + Text() + .aspectRatio(1) + .height('100%') + .backgroundColor("#ffe5e5e5") + .borderRadius(15) + Column() { + Text(item.desc + index) + Text(item.tag) + .fontSize(20) + .fontColor("#ff1dee06") + Text() + .height(COMPONENT_STYLE.SUB_ITEM_TEXT_HEIGHT) + .width(COMPONENT_STYLE.SUB_ITEM_TEXT_WIDTH_TITLE) + .backgroundColor("#ffb8b8b8") + Text() + .height(COMPONENT_STYLE.SUB_ITEM_TEXT_HEIGHT) + .width(COMPONENT_STYLE.SUB_ITEM_TEXT_WIDTH_BODY) + .backgroundColor("#ff8f8f8f") + } + .width('100%') + .alignItems(HorizontalAlign.Start) + .justifyContent(FlexAlign.SpaceEvenly) + .height('100%') + } + .backgroundColor("#ffffffff") + .height(COMPONENT_STYLE.SUB_ITEM_HEIGHT) + } + .id('content_' + index) + } + + /** + * 左侧类别视图 + */ + @Builder + tagListItemBuilder(item: string, index: number) { + ListItem() { + Text(item) + .width('100%') + .height(COMPONENT_STYLE.TAG_TEXT_HEIGHT) + .textAlign(TextAlign.Center) + .onTouch((event: TouchEvent) => { + if (event.type === TouchType.Down) { + this.isClickTagList = true; // 当前点击的是一级列表 + } + }) + .onClick((e: ClickEvent): void => { + // TODO:知识点: 3.点击一级列表后,通过一级列表索引获取二级列表索引,分别操作不同的Scroller对象使列表滚动到指定位置 + this.currentTagIndex = index; + const itemIndex = this.findItemIndex(index); + this.scroller.scrollToIndex(itemIndex, true); + }) + .backgroundColor(this.currentTagIndex === index ? "#ffffffff" : '#ffe3e3e3') + } + .id(this.currentTagIndex === index ? 'type_' + index : 'type_' + index + '_selected') + } + + /** + * 根据一级列表索引值获取二级列表索引值 + * + * @param {number} index - 一级列表索引值 + * @returns {number} 二级列表索引值 + */ + findItemIndex(index: number): number { + return this.records[index]; + } + + /** + * 根据二级列表索引值获取对应一级列表索引 + * + * @param {number} index - 二级列表索引值 + * @returns {number} 一级列表索引值 + */ + findClassIndex(index: number): number { + let ans = 0; + for (let i = 0; i < this.records.length; i++) { + if (index >= this.records[i] && index < this.records[i + 1]) { + ans = i; + break; + } + } + return ans; + } + + /** + * 监听一级列表获焦索引变化 + */ + onIndexChange(s: string) { + const start = this.tagIndexPosition.start; + const end = this.tagIndexPosition.end; + if ((this.currentTagIndex === end || this.currentTagIndex === end - 1)) { + let leftScrollCount: number = this.currentTagIndex + 1; + leftScrollCount = leftScrollCount >= this.tagLists.length - 1 ? this.tagLists.length - 1 : leftScrollCount; + this.classifyScroller.scrollToIndex(leftScrollCount, true); // 使获焦item向左滚动 + } + if (this.currentTagIndex === start || this.currentTagIndex === start + 1) { + let rightScrollCount: number = this.currentTagIndex - 1; + rightScrollCount = rightScrollCount <= 0 ? 0 : rightScrollCount; + this.classifyScroller.scrollToIndex(rightScrollCount, true); // 使获焦item向右滚动 + } + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/Example2.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/Example2.ets new file mode 100644 index 0000000000..9f0184c077 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/Example2.ets @@ -0,0 +1,82 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { + memo, + __memo_context_type, + __memo_id_type +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + Row, + Entry +} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins +import { + State, + Link, + StateDecoratedVariable, + MutableState, + stateOf, + observableProxy +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import hilog from '@ohos.hilog'; +import { Context,UIContext} from '@ohos.arkui.UIContext'; +import { CalendarView } from "./view/CalendarView"; +@Entry +@Component +struct Example2Test { + aboutToAppear() { + } + + build() { + Column() { + Row() { + Text("← 返回").fontSize(20) + .onClick((e: ClickEvent) => { + this.getUIContext().getRouter().back() + }).width("100%") + }.height("5%") + CalendarView(); + } + } +} + +export class ComExampleTrivialApplication extends UserView { + getBuilder() { + hilog.info(0x0000, 'testTag', 'getBuilder'); + let wrapper = @memo() =>{ + hilog.info(0x0000, 'testTag', 'Example2Test'); + Example2Test(undefined); + } + return wrapper; + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/components/DateModel.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/components/DateModel.ets new file mode 100644 index 0000000000..d7be0116e5 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/components/DateModel.ets @@ -0,0 +1,41 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * 设置项的数据类 + */ +export class DateModel { + day: number; + week: number; + month: number; + year: number; + + constructor(day: number, week: number, month: number, year: number) { + this.day = day; + this.week = week; + this.month = month; + this.year = year; + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/components/GetDate.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/components/GetDate.ets new file mode 100644 index 0000000000..e04e442193 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/components/GetDate.ets @@ -0,0 +1,72 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { DateModel } from './DateModel'; + +const SATURDAY = 6; // 日历表上周六对应的序列号,从周日开始算起,取值0~6 + +/* + * 根据指定年份和月份获取该月在日历表上的日期排布数据 + * @param { number } specifiedMonth - 指定月份 + * @param { number } specifiedYear - 指定年份 + */ +export function getMonthDate(specifiedMonth: number, specifiedYear: number): number[] { + let currentFirstWeekDay: number = 0; // 初始化指定月的第一天是周几 + let currentLastWeekDay: number = 0; // 初始化指定月的最后一天是周几 + let currentAllDay: number[] = []; // 初始化指定月的日期排列数组 + let totalDays = new Date(specifiedYear, specifiedMonth, 0).getDate(); // 初始化指定月总天数 + currentFirstWeekDay = new Date(specifiedYear, specifiedMonth - 1, 1).getDay(); // 获取指定月的第一天是周几 + currentLastWeekDay = new Date(specifiedYear, specifiedMonth - 1, totalDays).getDay(); // 获取指定月的最后一天是周几 + // 将月份中显示上个月日期的内容置0 + for (let item = 0; item < currentFirstWeekDay; item++) { + currentAllDay.push(0); + } + // 将本月日期内容存入数组 + for (let item = 1; item <= totalDays; item++) { + currentAllDay.push(item); + } + // 将月份中显示下个月日期的内容置0 + for (let item = 0; item < SATURDAY - currentLastWeekDay; item++) { + currentAllDay.push(0); + } + return currentAllDay; +} + +/* + * 获取当前日期,年月日星期几 + */ +export function getRealTimeDate(): DateModel { + const nowDate = new Date(); // 创建Date对象,设置当前日期和时间 + let currentMonth = nowDate.getMonth() + 1; // 获取当前月份,getMonth()获得的值是0~11,实际月份需要+1 + let currentDay = nowDate.getDate(); // 获取当前日 + let currentYear = nowDate.getFullYear(); // 获取当前年份 + let currentWeekDay = new Date(currentYear, currentMonth - 1, currentDay).getDay(); // 获取当前星期几 + let nowDateModel = new DateModel(0, 0, 0, 0); // 创建DateModel实例 + nowDateModel.day = currentDay; + nowDateModel.week = currentWeekDay; + nowDateModel.month = currentMonth; + nowDateModel.year = currentYear; + return nowDateModel; +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/components/MonthDataSource.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/components/MonthDataSource.ets new file mode 100644 index 0000000000..905719e28d --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/components/MonthDataSource.ets @@ -0,0 +1,150 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { + IDataSource, + DataChangeListener, + Component, + UserView, + Builder,Resource,$r,Row +} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins +import { State, Link, StorageLink, Watch } from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins + +export interface Month { + month: string; // 具体年月 + num: number; // 月份 + days: number[]; // 该月日期 +} + +/** + * Basic implementation of IDataSource to handle data listener + * + * @class + * @implements {IDataSource} + */ +export class MonthDataSource implements IDataSource { + private listeners: DataChangeListener[] = []; + public dataArray: Month[] = new Array (); + + /** + * 获取数组长度。 + * @returns {number} 返回数组长度。 + */ + public totalCount(): number { + return this.dataArray.length; + } + + /** + * 获取指定索引数据。 + * @param {number} index - 索引值。 + * @returns {CustomDataType} 返回指定索引数据。 + */ + public getData(index: number): Month { + return this.dataArray[index]; + } + + /** + * 为LazyForEach组件向其数据源处添加listener监听。 + * @param {DataChangeListener} listener - 监听对象。 + */ + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + console.info('add listener'); + this.listeners.push(listener); + } + } + + /** + * 为对应的LazyForEach组件在数据源处去除listener监听。 + * @param {DataChangeListener} listener - 监听对象。 + */ + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + console.info('remove listener'); + this.listeners.splice(pos, 1); + } + } + + /** + * 通知LazyForEach组件需要在index对应索引处添加子组件。 + * @param {number} index - 索引值。 + */ + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + }) + } + + /** + * 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件。 + * @param {number} index - 索引值。 + */ + notifyDataChange(index: number): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + }) + } + + /** + * 通知LazyForEach组件需要在index对应索引处删除该子组件 + * @param {number} index - 索引值。 + */ + notifyDataDelete(index: number): void { + this.listeners.forEach(listener => { + listener.onDataDelete(index); + }) + } + + /** + * 通知LazyForEach组件将from索引和to索引处的子组件进行交换 + * @param {number} from - 起始值。 + * @param {number} to - 终点值。 + */ + notifyDataMove(from: number, to: number): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + }) + } + + /** + * 改变单个数据。 + * @param {number} index - 索引值。 + * @param {CustomDataType} data - 修改后的值。 + */ + public addData(index: number, data: Month): void { + this.dataArray.splice(index, 0, data); + this.notifyDataAdd(index); + } + + /** + * 添加数据。 + * @param {CustomDataType} data - 需要添加的数据。 + */ + public pushData(data: Month): void { + this.dataArray.push(data); + this.notifyDataAdd(this.dataArray.length - 1); + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/view/CalendarView.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/view/CalendarView.ets new file mode 100644 index 0000000000..766bb16eb5 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example2/view/CalendarView.ets @@ -0,0 +1,283 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { DateModel } from '../components/DateModel'; +import { getRealTimeDate, getMonthDate } from '../components/GetDate'; +import promptAction from '@ohos.promptAction'; +import { + memo, + __memo_context_type, + __memo_id_type +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import { + Text, + TextAttribute, + Column, + FlexAlign, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + List, + ListItem, + Image, + Tabs, + TabContent, + Row, + ButtonType, + Builder, + CustomDialogController, + DismissDialogAction, + DialogAlignment, + $r, + Resource, + Entry, + TextInput, + Scroller, + Flex, + HorizontalAlign, + SafeAreaType, + SafeAreaEdge, + FlexWrap, + TextAlign, + ForEach, + Scroll, + Color, + Span, + ItemAlign, + Margin +} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins +import { State, Link, StorageLink, Consume } from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import hilog from '@ohos.hilog'; +import { Context, UIContext } from '@ohos.arkui.UIContext'; +import { Month, MonthDataSource } from '../components/MonthDataSource'; + +const ELEMENTS_MARGIN_L = 24; +const ELEMENTS_MARGIN_M = 8; +const TRANSITION_DURATION = 200; +const MONDAY = '一'; +const TUESDAY = '二'; +const WEDNESDAY = '三'; +const THURSDAY = '四'; +const FRIDAY = '五'; +const SATURDAY = '六'; +const SUNDAY = '日'; +const WEEK: string[] = [SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY]; // 设置日历周,从周日开始 +const MONTH: string = '月'; +const YEAR: string = '年'; +const MONTHS = 12; +const JANUARY = 1; +const WEEK_NUMBER = 7; +const MONTH_NUMBER = 35; +const GRID_HEIGHT_L = 360; +const GRID_HEIGHT_M = 300; +const ELEMENTS_MARGIN = 12; + +@Entry +@Component +export struct CalendarView { + private week: string[] = ['日', '一', '二', '三', '四', '五', '六']; // 设置日历周,从周日开始 + @State dateModel: DateModel = new DateModel(0, 0, 0, 0); // 初始化dateModel数据 + @State flag: boolean = false; + @State contentData: MonthDataSource = new MonthDataSource(); // 列表数据 + nowDate: Date = new Date(); + currentMonth: number = this.nowDate.getMonth() + 1; // 当前月份 + currentDay: number = this.nowDate.getDate(); // 当前日 + currentYear: number = this.nowDate.getFullYear(); // 当前年份 + currentWeekDay: number = new Date(this.currentYear, this.currentMonth - 1, this.currentDay).getDay(); // 当前周几 + private scroller: Scroller = new Scroller(); // 二级列表Scroller对象 + @State nextMonth: number = 1; // 初始化下一个月月份 + @State nextYear: number = 1; // 初始化下一个月年份 + @State nextMonthDay: number[] = new Array (); // 初始化下一个月的日期排列数组 + @State currentMonthDay: number[] = new Array (); // 初始化当前月的日期排列数组 + + // 获取当前日期 + aboutToAppear(): void { + this.dateModel = getRealTimeDate(); // 获取当前年月日信息 + this.currentMonth = this.dateModel.month; // 获取当前月份 + this.currentDay = this.dateModel.day; // 获取当前日 + this.currentYear = this.dateModel.year; // 获取当前年份 + + this.currentMonthDay = getMonthDate(this.currentMonth, this.currentYear); + // 如果下个月是在下一年,则下个月是1月份,年份要+1 + if (this.currentMonth === MONTHS) { + this.nextMonth = JANUARY; + this.nextYear = this.currentYear + 1; + } + // 如果下个月是还是当前年,则月份+1,年份不变 + else { + this.nextMonth = this.currentMonth + 1; + this.nextYear = this.currentYear; + } + this.nextMonthDay = getMonthDate(this.nextMonth, this.nextYear); + // 获取当前月和下个月的日期数据 + const months: Month = + { + month: `${this.currentYear}年 ${this.currentMonth}月`, + num: this.currentMonth, + days: this.currentMonthDay + } + const months2: Month = + { + month: `${this.nextYear}年 ${this.nextMonth}月`, + num: this.nextMonth, + days: this.nextMonthDay + } + this.contentData.pushData(months); + this.contentData.pushData(months2); + } + + @State strss: string | Resource = $r('app.string.customcalendarpickerdialog_departure'); + + @Builder + calendarMainView() { + Column() { + Column() { + // 出发地和目的地显示 + Row() { + Text($r('app.string.customcalendarpickerdialog_departure')) + .fontSize(20) + Image($r("app.media.app_icon")) + .height(20) + .width(20) + Text($r('app.string.customcalendarpickerdialog_destination')) + .fontSize(20) + } + .justifyContent(FlexAlign.SpaceBetween) + .width('100%') + + // 当前日期显示 + Row() { + Text(`${this.dateModel.month}月${this.dateModel.day}日`) + .fontSize(20) + Text(`星期${this.week[this.dateModel.week]}`) + .fontSize(20) + } + .id('calender_click') + .justifyContent(FlexAlign.Start) + .width('100%') + .margin(8) + .onClick((e: ClickEvent) => { + // 点击当前日期后打开自定义弹窗显示日历 + hilog.info(0x0000, 'testTag', '查询车票按钮显示 onClick'); + this.flag = !this.flag; + }) + + // 查询车票按钮显示 + Button($r('app.string.customcalendarpickerdialog_check_ticket')) + .fontSize(20) + .width('100%') + .onClick((e: ClickEvent) => { + }) + } + .margin(10) + .borderRadius($r('app.string.ohos_id_corner_radius_default_m')) + + Flex({ wrap: FlexWrap.Wrap }) { + Text($r('app.string.customcalendarpickerdialog_departure_date')) + .fontSize(20) + .height(20).width("100%") + .textAlign(TextAlign.Center) + // 显示周信息,从周日开始到周六 + ForEach(WEEK, (weekday: string) => { + Text(weekday) + .fontSize(20) + .width(50) + .height(30) + .fontColor(weekday === SUNDAY || weekday === SATURDAY ? Color.Red : Color.Black) + .borderRadius(10) + .textAlign(TextAlign.Center) + .layoutWeight(1) + }) + List() { + ForEach(this.contentData.dataArray, (monthItem: Month) => { + ListItem() { + Flex({ wrap: FlexWrap.Wrap }) { + // 月份信息 + Text(monthItem.month) + .width("100%") + .height(40) + .fontSize(20) + .fontColor(Color.Black) + .backgroundColor($r("app.color.highlyloadedcomponentrender_color_year_background")) + .textAlign(TextAlign.Center) + .id('id_highly_loaded_component_render_title') + // 日期信息 + ForEach(monthItem.days, (day: number, index: number) => { + Text() { + Span(JSON.stringify(day)) + .fontSize(20) + } + .height(50) + .width(50) + .opacity(day === 0 ? 0 : 1) + .fontColor(day < this.currentDay && monthItem.num === this.currentMonth ? Color.Grey : + Color.Black) + .borderRadius(10) + .textAlign(TextAlign.Center) + .backgroundColor(day === this.currentDay && monthItem.num === this.currentMonth ? + $r('app.color.ohos_id_color_palette9') : Color.Transparent) + .onClick((e: ClickEvent) => { + if (day >= this.currentDay || monthItem.num > this.currentMonth || + Number(monthItem.month.substring(0, 4)) > this.currentYear) { + let weekIndex = monthItem.days.indexOf(day) % WEEK_NUMBER; // 将当前日转换成星期显示 + let dateModelTmp: DateModel = new DateModel(0, 0, 0, 0); + dateModelTmp.day = day; + dateModelTmp.week = weekIndex; + dateModelTmp.month = monthItem.num; + dateModelTmp.year = Number(monthItem.month.substring(0, 4)); + this.dateModel = dateModelTmp; + if (this.flag == true) { + this.flag = false; + } + } + }) + }) + }.width("100%") + } + }) + } + .width("100%") + .height("100%") + .backgroundColor($r('app.color.ohos_id_color_background')) + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]) + } + .width("100%") + .height("60%") + .margin(10) + .opacity(this.flag === true ? 1 : 0) + .borderRadius($r('app.string.ohos_id_corner_radius_default_m')) + }.height("100%") + } + + build() { + Column() { + this.calendarMainView(); + } + .width('100%') + .height('100%') + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/Example3.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/Example3.ets new file mode 100644 index 0000000000..dfbaaebb4a --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/Example3.ets @@ -0,0 +1,87 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { + memo, + __memo_context_type, + __memo_id_type +} from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + Row, + TextAlign, + Entry +} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins +import { + State, + Link, + StateDecoratedVariable, + MutableState, + stateOf, + observableProxy +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import hilog from '@ohos.hilog'; +import { Context, UIContext } from '@ohos.arkui.UIContext'; +import { ShoppingCart } from './view/ShoppingCart'; + +@Entry +@Component +struct Example3Test { + aboutToAppear() { + + } + + build() { + Column() { + Row() { + Text("← 返回").fontSize(20) + .onClick((e: ClickEvent) => { + this.getUIContext().getRouter().back(); + }).width("100%") + }.height("5%") + + ShoppingCart(); + } + } +} + +export class ComExampleTrivialApplication extends UserView { + getBuilder() { + hilog.info(0x0000, 'testTag', 'getBuilder'); + let wrapper = @memo() => + { + hilog.info(0x0000, 'testTag', 'Example3Test'); + Example3Test(undefined); + } + return wrapper; + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/common/CommonText.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/common/CommonText.ets new file mode 100644 index 0000000000..6409ad4458 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/common/CommonText.ets @@ -0,0 +1,165 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { memo, __memo_context_type, __memo_id_type } from '@ohos.arkui.stateManagement' +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + AttributeModifier, + RowAttribute, + ImageAttribute, + CheckboxAttribute, + Row, + Checkbox, + $r, + Resource, + Image, + TextAlign, + FlexAlign +} from '@ohos.arkui.component' +import { + State, + Consume, + StateDecoratedVariable, + MutableState, + stateOf, + observableProxy +} from '@ohos.arkui.stateManagement' +import hilog from '@ohos.hilog' +import promptAction from '@ohos.promptAction'; + +const COLUMN_SPACE = 10; // column间隙 + +/** + * 自定义封装公共文本组件 + */ +@Component +export struct CommonText { + build() { + Row() { + Text($r('app.string.dynamicattributes_text_one')) + .fontSize(12) + .fontColor($r('app.color.dynamicattributes_orange')) + .textAlign(TextAlign.Center) + Text($r('app.string.dynamicattributes_text_two')) + .fontSize(12) + .fontColor($r('app.color.dynamicattributes_orange')) + .textAlign(TextAlign.Center) + .margin(10); + Text($r('app.string.dynamicattributes_text_three')) + .fontSize(12) + .fontColor($r('app.color.dynamicattributes_orange')) + .textAlign(TextAlign.Center) + }.width($r('app.string.dynamicattributes_max_size')) + } +} + +/** + * 自定义封装底部bar组件 + */ +@Component +export struct BottomBar { + @State buttonName: Resource = $r('app.string.dynamicattributes_settlement'); + @State barType: BarType = BarType.SHOPPING_CART; + + build() { + Row() { + Column() { + if (this.barType === BarType.DETAILS) { + Button($r('app.string.dynamicattributes_add_cart')) + .height(30) + .width(90) + .backgroundColor(($r('app.color.dynamicattributes_orange'))) + .onClick((e: ClickEvent) => { + }) + } + } + + Button(this.buttonName) + .height(30) + .width(90) + .backgroundColor(($r('app.color.dynamicattributes_orange'))) + .onClick((e: ClickEvent) => { + }) + } + .height(60) + .width($r('app.string.dynamicattributes_max_size')) + .padding(15) + .backgroundColor($r('app.color.dynamicattributes_barColor')) + .justifyContent(FlexAlign.End) + } +} + +/** + * 自定义封装图文组件 + */ +@Component +export struct ImageText { + @State item: string | Resource = $r('app.string.dynamicattributes_text'); + @State textOneContent: string | Resource = $r('app.string.dynamicattributes_text'); + @State textTwoContent: string | Resource = $r('app.string.dynamicattributes_text'); + @State textThreeContent: string | Resource = $r('app.string.dynamicattributes_text'); + @State imageSrc: Resource = $r('app.media.icon'); + + build() { + Row() { + Row() { + Image($r('app.media.icon')) + .height(100) + .width(100) + .borderRadius(15) + .onClick((e: ClickEvent) => { + }) + } + + Column() { + Text(this.item) + .fontSize(20).width($r('app.string.dynamicattributes_max_size')) + Text(this.textThreeContent) + .fontSize(20).width($r('app.string.dynamicattributes_max_size')) + CommonText() + Text(this.textOneContent) + .fontSize(20).width($r('app.string.dynamicattributes_max_size')) + .fontColor($r('app.color.dynamicattributes_orange')) + }.margin(15) + } + .width($r('app.string.dynamicattributes_max_size')) + .height($r('app.string.dynamicattributes_max_size')) + } +} + +/* + 枚举底部bar类型 +*/ +export enum BarType { + SHOPPING_CART, // 购物车 + DETAILS, // 详情页 +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/common/LazyForEach.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/common/LazyForEach.ets new file mode 100644 index 0000000000..e1c4e89ce5 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/common/LazyForEach.ets @@ -0,0 +1,171 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { memo, __memo_context_type, __memo_id_type } from '@ohos.arkui.stateManagement'; +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + IDataSource, + DataChangeListener +} from '@ohos.arkui.component'; +import { State, StateDecoratedVariable, MutableState, stateOf, observableProxy } from '@ohos.arkui.stateManagement'; + +class BasicDataSource implements IDataSource { + private listeners: DataChangeListener[] = []; + private originDataArray: string[] = []; + + /** + * 获取数组长度。 + * @returns {number} 返回数组长度。 + */ + public totalCount(): number { + return 0; + } + + /** + * 获取指定索引数据。 + * @param {number} index - 索引值。 + * @returns {string} 返回指定索引数据。 + */ + public getData(index: number): string { + return this.originDataArray[index]; + } + + /** + * 为LazyForEach组件向其数据源处添加listener监听。 + * @param {DataChangeListener} listener - 监听对象。 + */ + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + console.info('add listener'); + this.listeners.push(listener); + } + } + + /** + * 为对应的LazyForEach组件在数据源处去除listener监听。 + * @param {DataChangeListener} listener - 监听对象。 + */ + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + console.info('remove listener'); + this.listeners.splice(pos, 1); + } + } + + /** + * 通知LazyForEach组件需要在index对应索引处添加子组件。 + * @param {number} index - 索引值。 + */ + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + }) + } + + /** + * 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件。 + * @param {number} index - 索引值。 + */ + notifyDataChange(index: number): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + }) + } + + /** + * 通知LazyForEach组件需要在index对应索引处删除该子组件 + * @param {number} index - 索引值。 + */ + notifyDataDelete(index: number): void { + this.listeners.forEach(listener => { + listener.onDataDelete(index); + }) + } + + /** + * 通知LazyForEach组件将from索引和to索引处的子组件进行交换 + * @param {number} from - 起始值。 + * @param {number} to - 终点值。 + */ + notifyDataMove(from: number, to: number): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + }) + } +} + +/** + * 继承自BasicDataSource的子类,重写了方法。 + * + * @class + * @extends {BasicDataSource} + */ +export class MyDataSource extends BasicDataSource { + public dataArray: string[] = []; + + /** + * 获取数组长度。 + * @returns {number} 返回数组长度。 + */ + public totalCount(): number { + return this.dataArray.length; + } + + /** + * 获取指定索引数据。 + * @param {number} index - 索引值。 + * @returns {string} 返回指定索引数据。 + */ + public getData(index: number): string { + return this.dataArray[index]; + } + + /** + * 改变单个数据。 + * @param {number} index - 索引值。 + * @param {string} data - 修改后的值。 + */ + public addData(index: number, data: string): void { + this.dataArray.splice(index, 0, data); + this.notifyDataAdd(index); + } + + /** + * 添加数据。 + * @param {string} data - 需要添加的数据。 + */ + public pushData(data: string): void { + this.dataArray.push(data); + this.notifyDataAdd(this.dataArray.length - 1); + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/view/Details.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/view/Details.ets new file mode 100644 index 0000000000..e455de9873 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/view/Details.ets @@ -0,0 +1,127 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { memo, __memo_context_type, __memo_id_type } from '@ohos.arkui.stateManagement'; +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + Stack, + Row, + FlexAlign, + Image, + $r, + FontWeight, + Color, + Entry, + SafeAreaEdge, + SafeAreaType +} from '@ohos.arkui.component'; +import { State, StateDecoratedVariable, MutableState, stateOf, observableProxy } from '@ohos.arkui.stateManagement'; +import hilog from '@ohos.hilog'; +import { BarType, BottomBar, CommonText } from '../common/CommonText'; + +const COLUMN_SPACE = 20; // column间隙 +const TEXT_SIZE = 15; // 自定义组件中text大小 + +@Entry +@Component +export struct Details { + build() { + Column() { + Column() { + Text("← 返回").fontSize(20) + .onClick((e: ClickEvent) => { + this.getUIContext().getRouter().back() + }).width("100%").height("5%") + + // 商品图片 + Image($r('app.media.icon')) + .height('60%').width("100%") + // 商品详情区 + ProductInfo() + }.width('100%').height('95%') + + // 底部购买区 + BottomBar({ + buttonName: $r('app.string.dynamicattributes_settlement'), + barType: BarType.DETAILS + }) + }.width('100%').height('100%').backgroundColor(Color.White) + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) + } +} + +@Component +struct ProductInfo { + build() { + Row() { + Column() { + Row() { + Text($r('app.string.dynamicattributes_commodity_price'))// 动态设置组件样式 + .width(100) + .fontSize(30) + Text($r('app.string.dynamicattributes_sold')) + .fontSize(15) + .width(60) + } + .width($r('app.string.dynamicattributes_max_size')) + .justifyContent(FlexAlign.SpaceBetween) + + Row() { + Text($r('app.string.dynamicattributes_full_reduction')) + .fontColor($r('app.color.dynamicattributes_red')) + .fontSize(20) + + Button($r('app.string.dynamicattributes_coupon_collection')) + .height(20) + .backgroundColor($r('app.color.dynamicattributes_red')) + } + .justifyContent(FlexAlign.SpaceBetween) + .width($r('app.string.dynamicattributes_max_size')) + + Text($r('app.string.dynamicattributes_commodity_name')) + .fontWeight(FontWeight.Bold) + .fontSize(25) + .width("100%") + Text($r('app.string.dynamicattributes_commodity_model')) + .fontSize(25) + .fontColor(Color.Gray) + .width("100%") + CommonText() + } + .backgroundColor($r('app.color.dynamicattributes_white')) + .height("100%") + } + .padding(10) + .height("40%") + .width($r('app.string.dynamicattributes_max_size')) + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/view/ShoppingCart.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/view/ShoppingCart.ets new file mode 100644 index 0000000000..a6a0b75a73 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example3/view/ShoppingCart.ets @@ -0,0 +1,150 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { memo, __memo_context_type, __memo_id_type } from '@ohos.arkui.stateManagement'; +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + PixelMap, + ResourceStr, + DrawableDescriptor, + Row, + Checkbox, + Stack, + NavDestination, + FontWeight, + Alignment, + List, + NavPathStack, + Builder, + Navigation, + $r, + SafeAreaType, + SafeAreaEdge, + Color, + Scroll, + ForEach, + LazyForEach, + ListItem +} from '@ohos.arkui.component'; +import { + State, + StateDecoratedVariable, + MutableState, + stateOf, + observableProxy, + Prop +} from '@ohos.arkui.stateManagement'; + +import hilog from '@ohos.hilog'; +import { Context, UIContext } from '@ohos.arkui.UIContext'; +import { MyDataSource } from '../common/LazyForEach'; +import { BottomBar, ImageText } from '../common/CommonText'; +import { Details } from './Details'; + +@Component +export struct ShoppingCart { + private data: MyDataSource = new MyDataSource(); + + aboutToAppear() { + for (let i = 1; i <= 20; i++) { + this.data.pushData("商品" + i); + } + } + + build() { + Column() { + Column() { + Text($r('app.string.dynamicattributes_shopping_cart')) + .fontSize(20) + .fontWeight(FontWeight.Bold) + .width("100%") + .height(50) + .padding(20) + .align(Alignment.Bottom) + List() { + LazyForEach(this.data, (item: string) => { + ListItem() { + ShoppingInfo({ item: item }); + }.width('100%') + }) + }.width('100%') + }.backgroundColor("#ffc4c6c6") + .height('90%') + .width('100%') + + BottomBar({ + buttonName: $r('app.string.dynamicattributes_settlement') + }) + } + .width('100%') + .height('100%') + .backgroundColor(Color.White) + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) + } +} + + +@Component +struct ShoppingInfo { + @Prop item: string = ' '; + + build() { + Column() { + // 店铺信息 + Row() { + Row() + .borderRadius(5) + .width(20) + .height(20) + .backgroundColor($r('app.color.dynamicattributes_iconColor')) + + Text($r('app.string.dynamicattributes_store_name')) + .fontSize(15) + .margin($r('app.float.dynamicattributes_float_5')) + } + .width(($r('app.string.dynamicattributes_max_size'))) + .height($r('app.float.dynamicattributes_float_20')) + + // 商品信息 + Row() { + ImageText({ item: this.item }) + }.onClick((e: ClickEvent) => { + this.getUIContext().getRouter().pushUrl({ url: "pages/Example3/view/Details" }); + }) + } + .padding($r('app.float.dynamicattributes_float_15')) + .margin(10) + .height($r('app.float.dynamicattributes_float_160')) + .backgroundColor($r('app.color.dynamicattributes_white')) + .borderRadius($r('app.float.dynamicattributes_float_20')) + } +} diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/Example4.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/Example4.ets new file mode 100644 index 0000000000..afdf419d68 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/Example4.ets @@ -0,0 +1,85 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { + memo, + __memo_context_type, + __memo_id_type +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + Row, + Entry +} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins +import { + State, + Link, + StateDecoratedVariable, + MutableState, + stateOf, + observableProxy +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import hilog from '@ohos.hilog'; +import { Context, UIContext } from '@ohos.arkui.UIContext'; +import { ReusePage } from './ReusePage'; + +@Entry +@Component +struct Example4Test { + aboutToAppear() { + } + + build() { + Column() { + Row() { + Text("← 返回").fontSize(20) + .onClick((e: ClickEvent) => { + this.getUIContext().getRouter().back(); + }).width("100%") + }.height("5%") + + ReusePage(); + } + } +} + +export class ComExampleTrivialApplication extends UserView { + getBuilder() { + hilog.info(0x0000, 'testTag', 'getBuilder'); + let wrapper = @memo() => + { + hilog.info(0x0000, 'testTag', 'Example4Test'); + Example4Test(undefined); + } + return wrapper; + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/GetDate.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/GetDate.ets new file mode 100644 index 0000000000..abdabd60b2 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/GetDate.ets @@ -0,0 +1,80 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { + memo, + __memo_context_type, + __memo_id_type +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + Entry +} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins + +import { + State, + Link, + StateDecoratedVariable, + MutableState, + stateOf, + observableProxy +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import hilog from '@ohos.hilog'; + +const SATURDAY: number = 6; // 日历表上周六对应的序列号,从周日开始算起,取值0~6 + +/* + * 根据指定年份和月份获取该月在日历表上的日期排布数据 + * @param {number} specifiedMonth - 指定月份 + * @param {number} specifiedYear - 指定年份 +// */ +export function getMonthDate(specifiedMonth: number, specifiedYear: number): number[] { + let currentFirstWeekDay: number = 0; // 初始化指定月的第一天是周几 + let currentLastWeekDay: number = 0; // 初始化指定月的最后一天是周几 + let currentAllDay: number[] = []; // 初始化指定月的日期排列数组 + let totalDays = new Date(specifiedYear, specifiedMonth, 0).getDate(); // 初始化指定月总天数 + currentFirstWeekDay = new Date(specifiedYear, specifiedMonth - 1, 1).getDay(); // 获取指定月的第一天是周几 + currentLastWeekDay = new Date(specifiedYear, specifiedMonth - 1, totalDays).getDay(); // 获取指定月的最后一天是周几 + // 将月份中显示上个月日期的内容置0 + for (let item = 0; item < currentFirstWeekDay; item++) { + currentAllDay.push(0); + } + // 将本月日期内容存入数组 + for (let item = 1; item <= totalDays; item++) { + currentAllDay.push(item); + } + // 将月份中显示下个月日期的内容置0 + for (let item = 0; item < SATURDAY - currentLastWeekDay; item++) { + currentAllDay.push(0); + } + return currentAllDay; +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/MonthDataSource.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/MonthDataSource.ets new file mode 100644 index 0000000000..9bb33f5fe7 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/MonthDataSource.ets @@ -0,0 +1,178 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { + memo, + __memo_context_type, + __memo_id_type +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + Entry, + IDataSource, + DataChangeListener +} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins +import { + State, + Link, + StateDecoratedVariable, + MutableState, + stateOf, + observableProxy +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins + +export interface Month { + month: string; // 具体年月 + num: number; // 月份 + days: number[]; // 该月日期 + year: number; // 年份 +} + +/** + * Basic implementation of IDataSource to handle data listener + * + * @class + * @implements {IDataSource} + */ +export class MonthDataSource implements IDataSource { + private listeners: DataChangeListener[] = []; + public dataArray: Month[] = new Array (); + + /** + * 获取数组长度。 + * @returns {number} 返回数组长度。 + */ + public totalCount(): number { + return this.dataArray.length; + } + + /** + * 获取指定索引数据。 + * @param {number} index - 索引值。 + * @returns {CustomDataType} 返回指定索引数据。 + */ + public getData(index: number): Month { + return this.dataArray[index]; + } + + public setData(index: number, data: Month[]) { + + } + + /** + * 为LazyForEach组件向其数据源处添加listener监听。 + * @param {DataChangeListener} listener - 监听对象。 + */ + registerDataChangeListener(listener: DataChangeListener): void { + if (this.listeners.indexOf(listener) < 0) { + this.listeners.push(listener); + } + } + + /** + * 为对应的LazyForEach组件在数据源处去除listener监听。 + * @param {DataChangeListener} listener - 监听对象。 + */ + unregisterDataChangeListener(listener: DataChangeListener): void { + const pos = this.listeners.indexOf(listener); + if (pos >= 0) { + this.listeners.splice(pos, 1); + } + } + + notifyDataReload(): void { + this.listeners.forEach(listener => { + listener.onDataReloaded(); + }) + } + + /** + * 通知LazyForEach组件需要在index对应索引处添加子组件。 + * @param {number} index - 索引值。 + */ + notifyDataAdd(index: number): void { + this.listeners.forEach(listener => { + listener.onDataAdd(index); + }) + } + + /** + * 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件。 + * @param {number} index - 索引值。 + */ + notifyDataChange(index: number): void { + this.listeners.forEach(listener => { + listener.onDataChange(index); + }) + } + + /** + * 通知LazyForEach组件需要在index对应索引处删除该子组件 + * @param {number} index - 索引值。 + */ + notifyDataDelete(index: number): void { + this.listeners.forEach(listener => { + listener.onDataDelete(index); + }) + } + + /** + * 通知LazyForEach组件将from索引和to索引处的子组件进行交换 + * @param {number} from - 起始值。 + * @param {number} to - 终点值。 + */ + notifyDataMove(from: number, to: number): void { + this.listeners.forEach(listener => { + listener.onDataMove(from, to); + }) + } + + /** + * 改变单个数据。 + * @param {number} index - 索引值。 + * @param {CustomDataType} data - 修改后的值。 + */ + public addData(index: number, data: Month): void { + this.dataArray.splice(index, 0, data); + this.notifyDataAdd(index); + } + + /** + * 添加数据。 + * @param {CustomDataType} data - 需要添加的数据。 + */ + public pushData(data: Month): void { + this.dataArray.push(data); + this.notifyDataAdd(this.dataArray.length - 1); + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/ReusePage.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/ReusePage.ets new file mode 100644 index 0000000000..374961140e --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Example4/ReusePage.ets @@ -0,0 +1,217 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import hilog from '@ohos.hilog' +import { getMonthDate } from './GetDate'; +import { Month, MonthDataSource } from './MonthDataSource'; +import { + memo, + __memo_context_type, + __memo_id_type +} from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { + Text, + TextAttribute, + Column, + Component, + Button, + ButtonAttribute, + ClickEvent, + UserView, + Flex, + ItemAlign, + ForEach, + FlexWrap, + Entry, + TextAlign, + $r, + Color, + HorizontalAlign, + SafeAreaType, + Row, + LazyForEach, + SafeAreaEdge, + Span, + List, + EdgeEffect, + BarState, + Scroll, + Scroller, + ExpectedFrameRateRange, + ListItem +} from "@ohos.arkui.component"; // TextAttribute should be insert by ui-plugins +import { + State, + Link, + StateDecoratedVariable, + MutableState, + stateOf, + observableProxy +} from "@ohos.arkui.stateManagement"; // should be insert by ui-plugins +import { Context, UIContext } from '@ohos.arkui.UIContext'; + +const MONDAY = '一'; +const TUESDAY = '二'; +const WEDNESDAY = '三'; +const THURSDAY = '四'; +const FRIDAY = '五'; +const SATURDAY = '六'; +const SUNDAY = '日'; +const WEEK: string[] = [SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY]; // 设置日历周,从周日开始 +const MONTH: string = '月'; +const YEAR: string = '年'; + +/** + * 通过组件复用,加载每个月份的数据,当数据量较多时,快速滑动到底部,会出现掉帧的情况。 + */ + +@Component +export struct ReusePage { + @State contentData: MonthDataSource = new MonthDataSource(); // 列表数据 + nowDate: Date = new Date(); + currentMonth: number = this.nowDate.getMonth() + 1; // 当前月份 + currentDay: number = this.nowDate.getDate(); // 当前日 + currentYear: number = this.nowDate.getFullYear(); // 当前年份 + currentWeekDay: number = new Date(this.currentYear, this.currentMonth - 1, this.currentDay).getDay(); // 当前周几 + private scroller: Scroller = new Scroller(); // 二级列表Scroller对象 + + // 初始化日历中一年的数据 + initCalenderData() { + for (let k = this.currentYear; k < 2035; ++k) { + for (let i = 1; i <= 12; i++) { + // 获取每个月的日数据 + const monthDays: number[] = getMonthDate(i, k); + const month: Month = { + month: i + MONTH, + num: i, + days: monthDays, + year: k + }; + this.contentData.pushData(month); + } + } + } + + aboutToAppear() { + this.initCalenderData(); + } + + build() { + Column() { + Text(this.currentYear + YEAR) + .width("100%") + .height(40) + .fontSize(20) + .fontColor(Color.Black) + .backgroundColor($r("app.color.highlyloadedcomponentrender_color_year_background")) + .textAlign(TextAlign.Center) + .id('id_highly_loaded_component_render_title') + List() { + LazyForEach(this.contentData, (monthItem: Month) => { + ListItem() { + ItemView({ + monthItem: monthItem, + currentMonth: this.currentMonth, + currentDay: this.currentDay, + currentYear: this.currentYear + }) + }.width("100%") + }) + } + .width("100%") + } + .width("100%") + .height("100%") + .backgroundColor($r('app.color.ohos_id_color_background')) + .alignItems(HorizontalAlign.Center) + .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.BOTTOM]) + } +} + +// @Reusable +@Component +struct ItemView { + @State monthItem: Month = { + month: '', + num: 0, + days: [], + year: 0 + } as Month; + private currentMonth: number = 0; + private currentYear: number = 0; // 当前年份 + private currentDay: number = 0; + private temp: Month[] = Array (); + private step: number = 0; + private maxStep: number = 2; // 最多执行的帧数 + readonly MAX_EVERY_FRAME: number = 5; // 每帧最多处理的数据量 + @State month: string = ""; + @State monthNumber: number = 0; + @State days: number[] = Array (); + @State year: number = 0; + + build() { + Flex({ wrap: FlexWrap.Wrap }) { + // 月份信息 + Text(this.monthItem.month) + .fontSize(25) + .height(40) + .fontColor(Color.Black) + .width("100%") + .textAlign(TextAlign.Start) + .layoutWeight(1) + + ForEach(WEEK, (weekday: string) => { + Text(weekday) + .fontSize(20) + .width(50) + .height(30) + .fontColor(weekday === SUNDAY || weekday === SATURDAY ? Color.Red : Color.Black) + .borderRadius(10) + .textAlign(TextAlign.Center) + .layoutWeight(1) + }) + // 日期信息 + ForEach(this.monthItem.days, (day: number, index: number) => { + Text() { + Span(JSON.stringify(day)) + .fontSize(20) + } + .height(50) + .width(50) + .opacity(day === 0 ? 0 : 1) + .fontColor(Color.Black) + .borderRadius(10) + .textAlign(TextAlign.Center) + .backgroundColor(day === this.currentDay && this.monthItem.num === this.currentMonth && + this.monthItem.year === this.currentYear ? + $r('app.color.ohos_id_color_palette9') : Color.Transparent) + .layoutWeight(1) + }) + } + .alignSelf(ItemAlign.Start) + .backgroundColor(Color.Transparent) + .width(350) + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Index.ets b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000..79a7248ead --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,71 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +import { memo, __memo_context_type, __memo_id_type } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import { Text, TextAttribute, Column, Component, Button, ButtonAttribute, ClickEvent, UserView, Row, TextAlign, Visibility, + List, WordBreak,ListItem,TextInput, InputType,TextOverflow, Image, HorizontalAlign,Tabs,$r,TabContent, Progress, ProgressType} from "@ohos.arkui.component" // TextAttribute should be insert by ui-plugins +import { State, Link, StateDecoratedVariable, MutableState, stateOf, observableProxy } from "@ohos.arkui.stateManagement" // should be insert by ui-plugins +import hilog from '@ohos.hilog' +import common from '@ohos.app.ability.common'; +import {globalContext} from '../entryability/GlobalContext'; +import { Context,UIContext} from '@ohos.arkui.UIContext' + +@Component +struct MyStateSample { + private context :common.UIAbilityContext = globalContext.getAbilityContext(); + build() { + Column() { + Button($r('app.string.example4')) + .onClick((e: ClickEvent)=>{ + this.getUIContext().getRouter().pushUrl({url:"pages/Example4/Example4"}) + }).fontSize(18).margin(15).width("100%") + Button($r('app.string.example3')) + .onClick((e: ClickEvent)=>{ + this.getUIContext().getRouter().pushUrl({url:"pages/Example3/Example3"}) + }).fontSize(20).width("100%") + Button($r('app.string.example2')) + .onClick((e: ClickEvent)=>{ + this.getUIContext().getRouter().pushUrl({url:"pages/Example2/Example2"}) + }).fontSize(20).margin(15).width("100%") + Button($r('app.string.example1')) + .onClick((e: ClickEvent)=>{ + this.getUIContext().getRouter().pushUrl({url:"pages/Example1/Example1"}) + }).fontSize(20).width("100%") + } + .padding(15) + .width("100%") + } +} + +export class ComExampleTrivialApplication extends UserView { + getBuilder() { + hilog.info(0x0000, 'testTag', 'getBuilder'); + let wrapper = @memo () => { + hilog.info(0x0000, 'testTag', 'MyStateSample'); + MyStateSample(undefined) + } + return wrapper; + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/module.json5 b/code/ArkTS1.2/ComponentSample/entry/src/main/module.json5 new file mode 100644 index 0000000000..f95f9a6e69 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/module.json5 @@ -0,0 +1,64 @@ +/** + * + * Copyright (c) 2025 Huawei Device Co., Ltd. + * + * All rights reserved. + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice,this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS, + * + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone", + "tablet", + "2in1" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/color.json b/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000..2d7ab60c09 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/color.json @@ -0,0 +1,88 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + }, + { + "name": "ohos_id_color_sub_background", + "value": "#F1F3F5" + }, + { + "name": "ohos_id_color_background", + "value": "#FFFFFF" + }, + { + "name": "ohos_id_color_warning", + "value": "#E84026" + }, + { + "name": "ohos_id_color_text_primary", + "value": "#E5000000" + }, + { + "name": "ohos_id_color_palette9", + "value": "#ED6F21" + }, + { + "name": "ohos_id_color_text_secondary", + "value": "#99000000" + }, + { + "name": "highlyloadedcomponentrender_color_year_background", + "value": "#33000000" + }, + { + "name": "dynamicattributes_white", + "value": "#FFFFFF" + }, + { + "name": "dynamicattributes_red", + "value": "#FF0000" + }, + { + "name": "dynamicattributes_orange", + "value": "#FF4500" + }, + { + "name": "dynamicattributes_buttonColor", + "value": "#EE7600" + }, + { + "name": "dynamicattributes_iconColor", + "value": "#B22222" + }, + { + "name": "dynamicattributes_imageColor", + "value": "#ccc" + }, + { + "name": "dynamicattributes_selectColor", + "value": "#FF8247" + }, + { + "name": "dynamicattributes_barColor", + "value": "#F5F5F5" + }, + { + "name": "calendar_switch_border_color", + "value": "#24A844" + }, + { + "name": "calendar_switch_segment_button_bgcolor", + "value": "#FFFEFEFE" + }, + { + "name": "calendar_switch_segment_button_font_color", + "value": "#4e4e4e" + }, + { + "name": "calendar_switch_segment_button_row_bgcolor", + "value": "#e7e7e7" + }, + { + "name": "calendar_switch_schedule_point_color", + "value": "#ffababab" + } + ] +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/float.json b/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/float.json new file mode 100644 index 0000000000..5cccb549a0 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/float.json @@ -0,0 +1,60 @@ +{ + "float": [ + { + "name": "page_text_font_size", + "value": "50fp" + }, + { + "name": "dynamicattributes_float_1", + "value": "1vp" + }, + { + "name": "dynamicattributes_float_5", + "value": "5vp" + }, + { + "name": "dynamicattributes_float_10", + "value": "10vp" + }, + { + "name": "dynamicattributes_float_12", + "value": "12vp" + }, + { + "name": "dynamicattributes_float_15", + "value": "15vp" + }, + { + "name": "dynamicattributes_float_20", + "value": "20vp" + }, + { + "name": "dynamicattributes_float_25", + "value": "25vp" + }, + { + "name": "dynamicattributes_float_30", + "value": "30vp" + }, + { + "name": "dynamicattributes_float_50", + "value": "50vp" + }, + { + "name": "dynamicattributes_float_60", + "value": "60vp" + }, + { + "name": "dynamicattributes_float_90", + "value": "90" + }, + { + "name": "dynamicattributes_float_100", + "value": "100vp" + }, + { + "name": "dynamicattributes_float_160", + "value": "160vp" + } + ] +} diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/integer.json b/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/integer.json new file mode 100644 index 0000000000..eb56fd598c --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/integer.json @@ -0,0 +1,256 @@ +{ + "integer": [ + { + "name": "customcalendarpickerdialog_train_image_size", + "value": 20 + }, + { + "name": "customcalendarpickerdialog_check_button_height", + "value": 40 + }, + { + "name": "customcalendarpickerdialog_card_padding", + "value": 16 + }, + { + "name": "customcalendarpickerdialog_text_height", + "value": 40 + }, + { + "name": "customcalendarpickerdialog_month_text", + "value": 240 + }, + { + "name": "highlyloadedcomponentrender_list_padding_left", + "value": 10 + }, + { + "name": "highlyloadedcomponentrender_list_padding_right", + "value": 10 + }, + { + "name": "highlyloadedcomponentrender_column_padding_bottom", + "value": 12 + }, + { + "name": "highlyloadedcomponentrender_year_height", + "value": 40 + }, + { + "name": "highlyloadedcomponentrender_month_font_size", + "value": 25 + }, + { + "name": "highlyloadedcomponentrender_month_height", + "value": 40 + }, + { + "name": "highlyloadedcomponentrender_month_width", + "value": 350 + }, + { + "name": "highlyloadedcomponentrender_month_margin_top", + "value": 5 + }, + { + "name": "highlyloadedcomponentrender_week_font_size", + "value": 20 + }, + { + "name": "highlyloadedcomponentrender_year_font_size", + "value": 20 + }, + { + "name": "highlyloadedcomponentrender_week_height", + "value": 30 + }, + { + "name": "highlyloadedcomponentrender_week_width", + "value": 50 + }, + { + "name": "highlyloadedcomponentrender_week_border_radius", + "value": 10 + }, + { + "name": "highlyloadedcomponentrender_day_font_size", + "value": 20 + }, + { + "name": "highlyloadedcomponentrender_day_lunar_font_size", + "value": 15 + }, + { + "name": "highlyloadedcomponentrender_day_height", + "value": 50 + }, + { + "name": "highlyloadedcomponentrender_day_width", + "value": 50 + }, + { + "name": "highlyloadedcomponentrender_day_border_radius", + "value": 10 + }, + { + "name": "customreusablepool_title_view_padding_left_side", + "value": 20 + },{ + "name": "customreusablepool_title_view_padding_right_side", + "value": 20 + },{ + "name": "customreusablepool_title_view_padding_left", + "value": 10 + },{ + "name": "customreusablepool_title_view_padding_right", + "value": 10 + },{ + "name": "customreusablepool_title_view_list_height", + "value": 30 + },{ + "name": "customreusablepool_home_button_margin_top", + "value": 20 + },{ + "name": "customreusablepool_item_name_margin_top", + "value": 10 + },{ + "name": "customreusablepool_flow_item_comp_border_radius", + "value": 10 + },{ + "name": "customreusablepool_flow_item_comp_name_font_size", + "value": 14 + },{ + "name": "customreusablepool_flow_item_comp_des_font_size", + "value": 12 + },{ + "name": "customreusablepool_flow_swiper_height", + "value": 200 + },{ + "name": "customreusablepool_flow_columns_Gap", + "value": 10 + },{ + "name": "customreusablepool_flow_rows_Gap", + "value": 5 + }, + { + "name": "calendar_switch_size_forty", + "value":40 + }, + { + "name": "calendar_switch_size_ten", + "value":10 + }, + { + "name": "calendar_switch_size_twenty_five", + "value":25 + }, + { + "name": "calendar_switch_size_three", + "value":3 + }, + { + "name": "calendar_switch_size_four", + "value":4 + }, + { + "name": "calendar_switch_size_seven", + "value":7 + }, + { + "name": "calendar_switch_margin_left", + "value":6 + }, + { + "name": "calendar_switch_margin_bottom", + "value":5 + }, + { + "name": "calendar_switch_margin_size_twelve", + "value":12 + }, + { + "name": "calendar_switch_size_fifteen", + "value": 15 + }, + { + "name": "calendar_switch_columns_gap", + "value": 0 + }, + { + "name": "calendar_switch_rows_gap", + "value": 10 + }, + { + "name": "calendar_switch_size_thirty_five", + "value": 35 + }, + { + "name": "calendar_switch_size_fourteen", + "value": 14 + }, + { + "name": "calendar_switch_border_radius", + "value": 20 + }, + { + "name": "calendar_switch_zero", + "value": 0 + }, + { + "name": "calendar_switch_size_eighteen", + "value": 18 + }, + { + "name": "calendar_switch_size_twenty", + "value": 20 + }, + { + "name": "calendar_switch_size_thirty", + "value": 30 + }, + { + "name": "calendar_switch_size_forty_six", + "value": 46 + }, + { + "name": "calendar_switch_size_one", + "value": 1 + }, + { + "name": "calendar_switch_size_forty_eight", + "value": 48 + }, + { + "name": "calendar_switch_size_eighty", + "value": 80 + }, + { + "name": "calendar_switch_size_ninety", + "value": 90 + }, + { + "name": "calendar_switch_size_hundred", + "value": 100 + }, + { + "name": "calendar_switch_size_sixteen", + "value": 16 + }, + { + "name": "calendar_switch_size_twenty_two", + "value": 22 + }, + { + "name": "calendar_switch_size_eight", + "value": 8 + }, + { + "name": "calendar_switch_size_sixty", + "value": 60 + }, + { + "name": "calendar_switch_two_hundred_fifty", + "value": 250 + } + ] +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/string.json b/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000..067c799f67 --- /dev/null +++ b/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/element/string.json @@ -0,0 +1,215 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + }, + { + "name": "customcalendarpickerdialog_departure", + "value": "北京" + }, + { + "name": "customcalendarpickerdialog_destination", + "value": "上海" + }, + { + "name": "customcalendarpickerdialog_check_ticket", + "value": "检查车票" + }, + { + "name": "customcalendarpickerdialog_departure_date", + "value": "出发日期" + }, + { + "name": "customcalendarpickerdialog_button_text", + "value": "仅演示,可自行实现业务功能" + }, + { + "name": "customcalendarpickerdialog_week_width", + "value": "14.3%" + }, + { + "name": "customcalendarpickerdialog_calendar_height", + "value": "90%" + }, + { + "name": "ohos_id_text_size_body2", + "value": "14" + }, + { + "name": "ohos_id_corner_radius_default_l", + "value": "16" + }, + { + "name": "ohos_id_corner_radius_default_m", + "value": "12" + }, + { + "name": "ohos_id_text_size_headline", + "value": "20" + }, + { + "name": "ohos_id_text_size_body1", + "value": "14" + }, + { + "name": "percent_100", + "value": "100%" + }, + { + "name": "dynamicattributes_shopping_cart", + "value": "购物车" + },{ + "name": "dynamicattributes_store_name", + "value": "xxx旗舰店" + }, + { + "name": "dynamicattributes_commodity_name", + "value": "商品" + }, + { + "name": "dynamicattributes_commodity_model", + "value": "商品型号" + }, + { + "name": "dynamicattributes_commodity_price", + "value": "¥300" + }, + { + "name": "dynamicattributes_text", + "value": "文本" + }, + { + "name": "dynamicattributes_text_one", + "value": "文本1" + }, + { + "name": "dynamicattributes_text_two", + "value": "文本2" + }, + { + "name": "dynamicattributes_text_three", + "value": "文本3" + }, + { + "name": "dynamicattributes_full_reduction", + "value": "每满300减40" + }, + { + "name": "dynamicattributes_sold", + "value": "已售88" + }, + { + "name": "dynamicattributes_coupon_collection", + "value": "领券" + }, + { + "name": "dynamicattributes_buy_now", + "value": "立即购买" + }, + { + "name": "dynamicattributes_add_cart", + "value": "加入购物车" + }, + { + "name": "dynamicattributes_settlement", + "value": "结算" + }, + { + "name": "dynamicattributes_only_show", + "value": "此样式仅为案例展示" + }, + { + "name": "dynamicattributes_text_proportion", + "value": "40%" + }, + { + "name": "dynamicattributes_image_proportion", + "value": "60%" + }, + { + "name": "dynamicattributes_max_size", + "value": "100%" + }, + { + "name": "dynamicattributes_list_height", + "value": "calc(100% - 50vp)" + }, + { + "name": "customreusablepool_percent_100", + "value": "100%" + }, + { + "name": "customreusablepool_percent_60", + "value": "60%" + }, + { + "name": "customreusablepool_button_normal", + "value": "常规复用" + }, + { + "name": "customreusablepool_button_custom", + "value": "全局自定义组件复用池" + }, + { + "name": "customreusablepool_water_flow_background_color", + "value": "#FAEEE0" + }, + { + "name": "calendar_switch_full_size", + "value": "100%" + }, + { + "name": "calendar_switch_size_twenty_three", + "value": "23%" + }, + { + "name": "calendar_switch_size_twenty_five", + "value": "25%" + }, + { + "name": "calendar_switch_button_style", + "value": "20fp" + }, + { + "name": "calendar_switch_describe", + "value": "说明" + }, + { + "name": "secondarylinkage_secondary_link", + "value": "二级联动" + }, + { + "name": "secondarylinkage_secondary_link_desc", + "value": "当前界面主要展示了通过选择一个列表(一级列表),来更新另一个列表(二级列表)的选项的效果" + }, + { + "name": "secondarylinkage_nomore", + "value": "没有更多了" + }, + { + "name": "example1", + "value": "列表二级联动" + }, + { + "name": "example2", + "value": "自定义日历选择器" + }, + { + "name": "example3", + "value": "跨文件样式复用和组件复用" + }, + { + "name": "example4", + "value": "合理处理高负载组件的渲染文章示例代码" + } + ] +} \ No newline at end of file diff --git a/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/media/background.png b/code/ArkTS1.2/ComponentSample/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..923f2b3f27e915d6871871deea0420eb45ce102f GIT binary patch literal 91942 zcma%jXIK;3mNp0q9;J9tQ6L}(1shFzC_y J4lDn zMF~o;fk0?MN& s@*G$N *V-pj#% zc8%$pJKu3H6B9PCPuxW2f19*Z$HpUUF(3}g7#RA-OX&8^G6)=p#i`)Dwb3Nq8~qFn z<^fU=`t_De-dZt2UTFpm04@e4TEsxg1E>YY7Az(HB;|?ti3gVq33;UuoLwdZwaGAv z)BE$Ei{3EL!}7;J7f*)>%m4pcxFd_P_m2-Ym9Z%ej=O?&A8%5Q1~0Zm`)oxAEhEn* zq2oE4oF)6o2I|Fpq^)*F&F&`ru81qZL uc*j^>C5>P>|jIS|}3X4#)eG^57s9%6*|3|F;x+jqe=h|lyO425fl z6@cI6z>Hyv5uXtYX#y5k0aI_<_dNiVmwZCL?}ObbXPW8*%1=@B)oy#Y%c~4;8%x`a z%D9RB*Iq(EEN}n0)L0~$o82*;j0iF5PRBnE(CyzU=FS%kpKs`5BPyC~KTl;`htI!t z g56!(Boib)BOTAg0FZU*rL05 zkM$puN+9YiW1b0?zq55yMGvG?k+9e^uNu~T%kN{~pwPex$^-7uU|Z?^6m0nUP~^cL z%T(GXMmC)6o U}w0XN34`VHWH#pzq#0-s~`${^BQ zGsp)>*KTj;c9}KpOro`uZYH__;b_ah6KQy43luufrM8tsB=2Fb6I(~)N47qQoe5AH zN_#q|RJ@sun6ZN!7{dB=f0HyYic^KI7cK~{HM)rNVY8{r#uumMPyA{ZLnoNqe5X^Q z9<_t4n>rJ!2Zm{Zm7rROaRCQUoEqGGU*Nt;_0LKIjaL^VAOL>XBhmT9DoG(?;~8Ax zV-w6KHM^z;H6BT~^5oo+VsD-jS@TU9 ~{}5`3m{qUsnvy!h7yNmLCh9<-ZPVhE4O&CHSSRtrbIp!3fxTddggiU;0|Q zSRv=4Mu{Q?)=Y=)peNckC&Bw6i5&6R+Z;z{0N 4~ImXWTmk ziTDk*hHBCW>pH4RA7V)<0G}$KR5M=9!SUJq(%a2~v@VnGMq$5Pgv+A`Qg2I}sUn zl&;Sxou_%;KZA1*k8fBBTB44p8nn`hW|4))1%(?z#;LdRItfmRMDm8ft5#DXZ|nMZ zEJ0NW`+XMf(n$HoyvzPh8QR5l4}c?n9pQ2#Rc+mEQT|PCEuO^BM{%ofCqj|8WxjqD zhLu5r<`NXQi*V%0lU*&9H2vF;3V{aqDDNJB5FV&R#T;Ko11nzD(hV97(fO~fNtMJ# zVSD!fdNW%bzuH-cIx~g1E%`W3`okpJf`Jvt{mm?FIo=IlpkZLLzcI7uERy1%xA3W7 zN5oayee1(qp_re~+GqO7DGji8R? Ou+B8xatq_TYlmV)nSHeB=KD?H+N{aVsk{smEh*qZeJ z))M#Y+iCG1+v9Vjh;NK|)^I-h&1<8ss#LY=%HHUfe$n)L1gzbr5@RYy77qV_-p*sO z(vx79H1@rk7pm)+s==EHddT)b(|76W)l^u^fLJY`7N-3f9h41;xg+w1JeMO@z^WHJ zu^~jzE|&DU7y|(`@A8PQG-c>q_Y6WHqf6+4C1QJ73VDy6w?TOj(%mDP!bgVkNG8Hh zzcmwnNnka8bZQ(Z<=i!Y@=C?_6J*t Le|0r>2Gdp!#iqDIUw^UmKuqLG97QbF&7q8+Bwr%v!=i@ly^ZOX}PD;Vr^ zTyljDx$VWI>o$@??c(-fVG-EobYv05?LZZ{-_o1Q`sWomwcFgB=hYZ@I^Oi~c`gLU zO&Z+3oaJeW9*)&5*z%`KU;|G^-t;OGn}wL#dOGZ|0TC@n@K<5U{`5iE)n~KDe0h*| zK#S6KaG+2>7}_$C`$b>X6+jx2*>4y $U^6BNmBT~V|8L}t1_V{Yu?Ck)-JZ+#FLk}R_D9mrH3mc7e zJt9SLjH+y|)bjsO8Qso&6#Vd9oiNO;$*cmdCvhQ~aJWKTeuUPt)LPO2d`B5Y&c6mW z)YQF5&Z(?mqJKE|%9uCY9PQdVM@$_oZgY3^RY^h>id7ajQyIa4sZ52c5F;%d|LN3G zj5=`HF-(yIR#Uf$wa1`3rCD6r*r(XAicvER!fw =i5Fy_DCahzZ6xa(D8RfC zL_q7dL745qWAMP2WJOVjIu)#1!~+&up&b&qT%G9?fRUk&1_&;#Z_?WkNG8P)FSsVO zX2vfG=~PfqoPvKh$GSQl__x~3tsOSY3-CxqCwHYW6BtMty;
xMBg>qTY((4 zF=`QHuipO^T8;&N>=} 6z#kQ+r_$N#M&r0aJfXQPOA73%&9|rL zVt)$!hzNR*fUVEE&7gr&LFp0cXhmnhjU;)VSeFYkuUyvV(8Fp*Q8}potdcr<8N|m0 z8IU_QP=)xubFRdu_xdZ5+Qd=VxQ{}?Nj88NySLo< ^s9@@&q^5S17=l?++g8RSr8qPeEo30h18NnD!tjDU3 z6z %#I4VVmFQ5!l&N(9i#_nK)4K=$SL7g|j1lK;iEjKrMPwO%T*QL% z-j!aTy~MG>A0Aqn|7@{@*S zDMoRwd1C4>d!H_%>9`Qfk0FS$E~#rGg{T&9TVkroUTgXOzDN*&X!jzj4|asP^S?57 zo)-!G(FB7ZMeU>B24bHjF7JpxU+%GfzWnGf*6+OIewh)aZjmd#iKj|8JvZo&&_+(V zGmmN(r7(kaZ|>c>ao v$yYB$2!j%Am`^?j^sco5`v*mG(=o%bvdyeUbC?lb5&d z%UKCu41wwotE+1(=s+>CI*gvHYC }kb2I3r2&k}3+*;M$!3Xn? z(Vb~d{}=K>j|{o&pEmQMf@gH)xk%?vA!FR !j|0m>KAckaYc*SdODE;HEmG5%~q#J_}ITGT`BJ`miBS>ui?SUI8Y6P*Q>$otnZf z2lCtF)rcg6=$K`D3>!h&tmk_cQ1|jFpf^X&w&q+m#Kzb$GU6RVJz?+?6B5y(9KM$Y zYn$>1?CaH(MxNIWKRPy}*4fTI+7C`5sorgyJtkLf5>+;TG)}YONvo5@tdS6LsisW_ z(wl=vAJ=?ORTlFB0yeH*djK?Mu&Bcq+7y0?)=c)l19}sjYTh1eIQCPfpyu {*64@KqB0mlsKZ#}K@7KT>d|xcDCirH zh4i+!#*!Bxexqo(J3zFrv4|g34GXi}Bxp~(d+B@^(0M}cA84 z^Tg;xRq+Bc!VEmLd~!wmVyaq5bw<9$!7)yM&NR72C7C}#MtH}5ELy (!j*SVu+nPa$o^~PShiG7YXY#RjJa5UuXCTe~?}v3y zYmj0&lH7JIjrCuJy*%(O!PiZ6m;y((bKo;A+eU>uh9;99%nSbF(qg!c`!S z7k}q?l)Qio5r$sksn|x^6S#moHlo?hu@dbix HKJ3cdG^VL*sG` IAQnPaK7Ff@<9X}CZa_9S>A zN`y+8yps+AIKO73R6~!*0bi9iLs_VhJl0NF7_d8HUKyLo3M;F-2N;FqYM`CXT}FQy z9cEc}Tp9UC ` zpOjW2>)Zen$89)goE_)V6?VS@h>5m<<-zf3KurXOw-LCcv9B^(rG!5J`s0H;!&R40 zw6roRCGUy2 )@Y+E98jx@Vw`6?M%J;WTfxiv;49Gh7L7yG7Omx) z0CUU1|7 jKBDzU`&ySgh4FAfHw6 zu*I=#3|)-i>#`UW(a>Rw@Jei{l~=+!;|qU2WxPLimNeZ@gI7T25(T)=D(IlGY&sOl z3P&*j(a9X`jBDdyTm;D8AGcfh^YZsA(}F&Gp71}>oi(z4AKiy!ox&(%RR~Sft_D~$ zFv4!Fjn -5b`WAq$ uX9L#T4J(HcGtjM$c+)7M5?sSR%vU0cm4XGZAXymv;1rtL#VQXc#|O0_IKjNfF~ z>BOK`M^)P)163{TvWPQ7HmPuvBo91LyKf6p6Z&Il#Pj@#;Qp{N{pN#FgCORiFD&rd zDXoEsoV#y @w>=?_|2*c1RwEi_ S;BVHyH}8c4_sJkk706wCIxCgiifVQI zj_m7z$W@$TJHAP*W~wo*%z~W4pRr2=E-QREYIio;$Pn{yvt@n>$9)njFP>g;w{9pE zJN)58;c^Y#G8GQ#*N_R~w<$bsq6visNxj8QN$$dnAoZ}Ua=2 6)X-R2jDNx^aKg2BJcY^TIx~VDEpsO^cjbYqg(4z)IUmIU6Mugp0STm!@44vB# z;Y45lr5@?P`d(~5` ^qnda=Xv{#ZEW`2Cr}xth8Oa|EyF^vg2;2ab`{!fr zXoIGlD%Qx2$O;o*x}v1<@a=FgLQ45JIm71#-5B(|Jclm%MmM+J--8({tgQO4phX-F?s)v0u(sWY5`vKT=23) z(_6yB#kebuQvniNLXnqzUq6{|-4O&JUnNy@naFo LiDlZK_MH_s7TT*debiS4 zZ^_oGY)Ke13NIdy4N2Uj1bv&F&PLRX8Pg1?K!X9#D=beo+)oT|B8 %8P<9@ff;d%jG^C;*bv?_2 zCcE~Q?vWE*5PT0UKc}3} Nm=7olHga@7G X=jS<@4b%tOjL@7X 6 zBg~9ESb(TefW3-+Ti{LLUD}9-> #&{*KHUNc9=`f@w+4xiy28zoFtdF-#nkpI>N z2x-?;y^sAQ^+CU^My%Oox6!%;uqc0K?CK~6D|&(ZxD#_;QW+gYQrzJ22&4=0%`WZ& z$Kpo^JgxP@!ZYqoeKn18d`sY7s~5Lj`xBpUI21pfJ`)`Tm+|KZ0~IT)l!YAFW~z#> z?L_;)md2vm&CW~ hp=tF%RU1 _VMf5ZeygZ=SO>RAS`zDj-QT(^|_&^CVnZ#hJDRCcc6zM%BK z5_ss}nn3?8fp77r{ NU*5uoamhQclBQsueYgH7%%J;?)&cRhQ0FX7TyIO zAqV*0i&U_ZtEzC_U&-C*4D*^HWA-!f;pe%Gmv{^^tmuCcB>^XC(psXV7pn|KK&2~p zw^s??(QO;YlBPkjGM-ajKP^G?0op_jWnnR%mjwx&&OhvUq8^#0oO@67&6>{e87(4Y zEW5WGqIHpBGn;|x35X}(r&*00)rD7IRzjYj%o)?J-S~^Sx6X!pA9A`16MEY0+*X7E z?Swc-omN{k?v`*BVY2PA=Sz{{_ XdIQdam=tmR~iX)zeAAy-YYuXqP{_R#E}%%TUp*C zR37u6*8~)Q2p*CIMDBt{wy_VCW6Hu_eUI+y8x6IWW+@UgbDT|Ins%zhl!(odvT^dX z6nlKfU!&G0kZo ;Z?r$S2ul4=Ou&JKjEDfd!chE({i2+!>&Pzy^|yMY15aU@^!q}(E@mrxXO+Y^ zl|CeVk@kFJ??PB8&$BE?94#-94F1N}%QK~SnpQq)#9wd`If2VqIlc%m95rZF^s*AZ z@Z(C|i+!+BR~`gspb@ZRfIi77;6zZ~Ii4%P|NK08QrY!8UuLg1nz%Id^;>lpnd7+1 zrE_-ur6zD+>1}6~F#~!j-(=|y0g?l$89rSEnPZEwhAO@FYdxSx+IR6=!F4Iq84AIb zVx+q=&xg1*1W8S1W@tCDZ4r6K_E4{omTKW(Kjv0TDZ;JVtrGbTrG;K@KA2YYGvO@q z$zWtgRAStrWxC%*+S*UJHJUD}4!{uZKi& ^a#1DpC4Jt631Z!Y0N2mvYBe z`^bqc -+GWIZ()gY#3ei%%Dox=f!x0?~DT1sqS$hqPC-^fyvcHGZUkX zQ*TB(UZyShhegM1T;_cUFA*zv`tr 7JP^V`^tF`d-9~$Q|r=r#M+)T zgqfkgx?NW)>?~Q4_bd}Le|C?*DO=ZkE;G#jq*fPkK?<;tX$R0UGIBqYFC7CzVlELJ z&js}Trx!r^;kgT_5JPK#Bcj1knKX26`M~ssqY+vzz+fVNAh!@tzijIji6~oeqZOu< znO4S3?!hAwH_E8ZQpmN*042Nv%!|(K{=TY_R_Lb~D#xiY#^A@=8!bPoy#@L<_z~C> ze*s@Gbj5T({u=fEmAgV1RRJvT)$J1;7c1mLUIM<*v*SWf+F#b(*_?TmPvCaz&;xHt z `zr|w>pkQ*qdzbi4C7-na4DyYGg4=k3yt~iwkd|sIiD3p1mGBoW{>K(8nigyO-lC zV!iui?#zVc7cLOV7A9Y5@{b$BG`t9T2LZj-K%3?jDi`JVPgM$3!}6H|{D}7Yl5z4W zUIC}%3=Kiq`!5d8V$Q9-rTTYFE>_9uBL~Z63V*Gj!f_{LPB#@o)*9#jeCFNNC!tsU z4BFfSX}ZPUg1IpW0jSCigCa-L$%g1_ZG_)S5wO* $=3Wh(>e=p^LR%sR z!mHyE7<`Y2$=qX=6S2%}6=QOg%2cf})ibASbwm$g)+6x~V}Ucp2y!C?sf+7B@ w`K0jS&Gg-%%6j;2 ufl$N8rdw~qDD%IMxSfg|La?+pPnkBNP}=QjS8upul@ zkz?YtFU@zml@qOhJA@4&QOsR=>6bkIZ;V2DmTi8lx4njiOktl))rr#BPp&~_Oxc_u z5eIHxVT0SG#B-><-VO;K-}qXc^KMb3?qjw4E23j+T(qMm!K?2^^_B4+uHut?Y&^aj zd2oAv)KPwqy~@^90_bApwj3Z4 9tefzo`UI1)v73oL?-9f}>NjDB zmTn!i1!D;##^c}>Z)gv~^5rx8tszqw20t{9cFrcO^}I2EKlM~=ZV*6%Chb*&d$U3T z+PxwW-E;7F;y!WZA5D`&wV2r36PC^_q5E|hu7I^xR?L{p`K{MAh%iNF?{Z-7$UCVL z^8mbhB3svg>qOslREMR$S`Zc^DygmRaJh@wImcLy-YYDEv=pEYdwuRF ecpwtx z16P n?;vauAp@cxrbQF$kk#mn R(1e*DbH0p6{z>7-;P^4K_3H+}Rt-4qTySu3VKE12n0D988#amAK_mHr>)4 ztT5NGs=d-fGvPe2sGNwu2R1R2#>M49*0b)JX6v`OkAP639WdYheY#uZEe!CrK#~5f zIhnX32&t`8(RShCeE^kbAphmg3C$Z{id=Yw>8An1Cmw9CRY~<-h=?q#vX;Cg;||Jb zyNLygTYk%HZ-xfiRvUJiVm1n}_<-AQSWHS<#Fki=7!|@T5}+ >tN7f({q-kz}UaM_^7|+{+8n7O~Kl;7{a~P8mkN&2_;wUv(*Z zZlPF#dpF6}`QO_rMub^j-Yp`0Lk-)@Y!_w~=n x4jL+I#XJSgbSIs_mwdt*lRc@Ct~Z9sUmrHGA>M<@f|gb0E=!Ep!S9Na gI+)siMTFf8M!)(MZ9y#N>RK$Y`;U=xSQgTi zeE%Pc#95)ZiN{+kgU}X#@aWsw2}|ACv6Ip_$aCXcWUOzK`^a*038i4OZqz8E@6{AL z&uhiOh!UUGNeVak$la5TDLY0DuBO_seCq1p0xq9-9e*}EzJY_}K{W1TMHa;YNa?A$ zJbf3XIvox7>y~>fL=jR|fnrtMW}840T)^^4_3$4%rvYHwjz!Sc!Zr!Sv33iiF#Zoa z!+$K{$bSI}%iqW_T>R;e@s;-E_(52*#wE4XS2}aRMzTZ>2Z7+VN#(;V`v`w+z_kJf zu$y%@bEbVT9dH_W$OB@%wyf7p=V%)#!aI41WvQ-ly1MP78@0eYS5}+}kC| {t^;-z>F>XKk(wBbaubnJy46(5*duwsOF z&LHd~I8Z4ntQpFY$-oeW0X3z*pDWq=AtvA-!w6?W#pZ%4_Yvv_MtNgbwrAL8Jis&s zdziD!0;j*ESwxu&fc7Zg?Nc3q`5QOba`^j5&!>RVdZiO*+3uQEFy z?MT9%xduJ}@lN%?BQp^3QkPbAXm^gxMBU9u&5HP>Jjg10r7UOX>{Sod=f6KSz?dNh z!evY?ko=^VLhG7fWw#B+ljQs_Jgcds)%H>`jZtsW1Etl}K{)SU!O;kq8OVlIS%hD5 zTMws^Mr6FTzI*0hDlaBmwF+A6V1#9~yZlPTEG4{;ZNS0kLBq|u&AQb`XcI0tu$UTB z^*rk(5v7a%*=ZCf`R~0sSMphp+1YO0n0Pg(a+phnN?u_H)c4*SR!8&atx^GXXX49o zt%q}tUKRN9FdOcTZxt(m`A`>99B->`qB<`MQa kd8&< zlbH*sVBvj{6SZl@lpQtlmo6`XG?d#Wqq(f1VDPP2a|Gh9)k^frxvt%2#|}l0>$=ic zQx#_VDZlrML{%_tJU#kcJ{#!-<*F+)g<^ez->zt>`U!}#w*pkrlYEaQILCra=a> zklx?zvb?&j=OE&|VwwECnA%gH k`q7 z#2;U78GYBqb(b)RU1jQ(VPghG{o3eEkT+C12Qi;fDBiUasLp&a6Q3*l^}x@z$?i *rg9?F;Yr+QA*&RqysvmG#5DJeNSxXn+TP2!8B2PE4vgAbG(dhdIu{t< zLoMl~)I$JTj6ALZeXd~BoFK(#I??xkP1D^+SoXV~RHPR!lx8O>sIU|WE??GqBwD5v zZalV7TsSrA?Z{e+YX7aqQuPhphn1?{cJJAgMY1zvE{zX>IhH)*Y-Zw+@TKL{LT9Q* z+0>jn;kED1SG7?te)Y38hJW!u)moHLSUm!w_G8`x)5{UuBkffnmY+=RKNfM;qGedz zlNsRt(gJpz-^6&@ht5Au+cnHC<#T-iv?0XK-skQ*HbT?$3TjjOvq_t|L%qoM67Mw8 zo=D*41DYRzL$s$5$Q_}-%V74VFSa%q2`Ep ZbRyM%hRP*IMl(&wAd|;St z*r2Qv-*mRvUGR0w3gpIXFJF;!iDx*L+XLdZ(*#J2M`S3V@Guf1p2ld-jCKB2SMYDk zK_y3)PCob{vgPc0`m@2GPOh9b4|k@d>9r`I%}UbGIc0N5<;FHI4%H-l;DoQzo%%Sa zI>`8jNe@)760aNG^9$>)VvIta;=No68cdfiSihpG*E14mN7@Ib)wRDvz|5!lnyaj4 zbMViMvTNnd@tczl%H%WwVkV)7>a=y(V3KSn=R75Tmttlk6adWe@t3ccxg%3lp+yX6 z@XBh(cqVu!kLqNo!-rN>w6(f{UxrSkw%xK}SOdPt1vVCR@3@4z9fg@7dkZJ8|0A>3 z79 j+ckQY9^QV~G! zuKP-&@1Y1{C~WF#9fkv%C+~6tsvKK*%uBc{a>=gusDYGm9$*m(*1z{owy(BS?BOLX z3|6cQ8;y9D@m)WYpdG0{(SES~80{
>Cp*DPrQmPh9zITa9;G2eT3=xhuKfY%RIS%h7?BJZ zT_bnUJsoDR0;ms6QSKK34HVTiGZ7yk!^|fKg7FDJtvpx_8}WPP^K6biAP$kJNNS2p z_I_p?ilgmc1`wT(tk7vtM4}|;v+YfSvd+0=GiX^UZ1iON8VjhR(9HS%jV~i<7UR<% zC1TF0KywgNw^(PEZk-R#Ea3oocd38b-zIW;X-u)5nrL^rz1=vR26TwDSw8~0DL!w! zi -cDl*H+ggp_(o>cGt4;)jt5Ps21$?J~umMz4FBTU* _3Ys!@X** v44Efz z_--rQCvn&D^**D2Ux@?!35YxCtD3C76e 3BfDp z834Tl@Mv#p#6FEqqI~GBuC%P^pHx3c&vscPTDNqCHOpp5n)9a6N8hHYN4yrA`6}Xf z=yglf8iLu(j%%db0Kc`Mks8cdgs}nL{_nG=`La}Wthkr0Mdq(rL%(v27mPaVSSK@; z4NbszRsA@TokBWub|pp5S8)XO0cvG<$NP5<=#90tMoSuh`xeq>w(iis+#=ryf@E8z zh1sO9{d~3;H8r-)FQG%a#I%P|?b?r-heNrxsc&u3BLTelWR&Lp4~leXbCslV!>0&u ziu l@YTcWs{rc%E=N(^HH{ZM(TL zvDTpF6|)PH>6!V2{}XA|AZVXyfvPnZN$&b_CF$r9*v3Q&qnZxE2=5~0Qz@&Q#AR 7~ec%T+tO@JV!v^3fZPns~ zbCPYJ#)v4 uhBkL6Tk0v;7?t#Y$JLjU@sw#g8P0L;mOG#7bavc zlA&twBXooTY@L+xo`Yfz@EH_&*!5tZe(65d9nB#yx9yUi#~Ql_yUL|> v^d(I#Tp>td{g%GRJ)?|62lEbIR?3M z>~DU8$-&@Zh`r-D$zO|Y$5Z*&nycTaoV^E@RTF}&ol@Z|`Xh6c4k8KsFp^RyvWMHF z!&EZZ-u&*P5QA=Y8;L)qp);pcWXVB`5Ld!HutdMSSUec-av@jk_7EH+TvO)+-F+7` z!b>{|NXh-H{CSh23Onf{z;QOgr4V=`QU38Iy9dC8lVOu(aNYh(cK(uOu%+{{&14Gp z`kJ;WLA=jz4dHTu4Uo;4A9TQcv;Rh6I#DhR(cW9QVAFTBpUpl(PpYp@a^vQ{)iEph zvjyvHlFH{_A1zPj1ID%m>>g%M3;osnpyP|0umy*Au|8?|+<+(VYj_F7ZRhoz3u$_e zsI2_$?5cKUdvCMKinKI!8uq#ZUq@*>dDXVW8bDNVEj(G??h1IW|Lv#LF{D7O&JTd? zF@5xumVrp=@}Q}Y#&1shrvF=(1WHQ2GId{qzTuV|@BO15<+2#3Js^H*E-ga3;ke$$ zh3RcW2=nf6Bo30(EC`Rggf2i!4?P^t?($ z=}mRUyvpk`2r7RyP1uU@O#CX3#}g76yLNE1*SNXz2+Mf}d>uGmWiGvc&Tw)4LS)eF z5^h$F;mH%>tj;X;T1t^CgIEVzTo)z6$gRo*uy&8DZ=&GE?P)w=d+5j~3t{iy2hIET zd>%(4Xp;_#Z_b!3?SjVQ4dUBrF01}qYo9l$3@)I7!RuY%WA8Z3Idzkdal}hEe+^2< z?-*veYNxi(eO>TW;d)pZ({+4fd8Ljy0fO&*lt8K$R=q-a|EON3ZJSi)V8({7bl5 z4Ssy}So=wm13&~CH`D23*8s@unZzxbG1(`< vv5iJlSX+K>Ve>rQXT!tbM %@i%qpo6#Pt|D1@WRl8fKVVHWY3CAA7?6@pz4KJvy9|yBN2oylE*perBVT5k zEoT#7YV93|DAKR~;Hvih{$-}mjc(5D;dC`7nh?I44R >gM_sIP z?FP+Efn9^4kCXXph}*a0dBRi%*!d>RGf{CKFd%%ai;M&!q&&wwKhr}&H0O-QAv=eH z&F5rr?%*CjagKRKGU-KPLSXC?J`MZE&JecFH1u=9zW(_L6UF9=fHBKQ#~C$IPt6p? zfK2L`y;H)(7&bA6di$&0{8g1Y7lzO@u-kdvLYfN!Jsb3%qlK~9QtyXEV4|v4OK&4r z8)HuHBj! zS*Y_YH+AOgHM#hy0^xy3&5`E1_~Q{8s1ZA2Lw_8O(v2$d5Yl65GGR{AZKoZXEEr#k z=7ueO^QQ%tK)i5oMGKOg&YE03B@-mHc8S`47k%C?il`VTan`NaJmqBCU@XRYeC07% zkF9RIa2{x|u&5tkF}C~|jB-B`h+vybZYRNW^nLVcm-~wmyqSje6^|(+i`j_7ws1;! zJYs`C#Ps_zEw>Wlz|kGM|2Y& blfuZzsO-#hSal7Vu=O1lf-XWIcf^4NJmruso%zo>8LIG`8Ccw8*eEVzaxTueVSXtoi=k%9lpF49}l=@OW!n}}2iN9D F+M_lVz8k~ktPRCU41ghTq7tF&LazTGFW4W7RO>;qfNDQ*r~%#rCa zjB^ge!LHnlf06#E>i7}((sb|{&K E;5`kMd zmZ=8RUzu(R-VSDUR{g}~VTmK6J}iqM1lJ}3div>Fzm(?wn+UIrQTnL)!bBbJ8_`l$ zSsgQdT0=?Mjrh)Wf0)wb33slb1gp+HgIYjm%w(AMh2tzzT!#jO3S}R17@M(Y^=hp- z9Www?Nhk{#(n1w-9QjbdS1d;j7?zJ;)=U<-nV@~+LVZ4 +Tze`7U(pio>O1Y;o>J!_q4Z`pVpg`9PKYAunj>~4~=t05P z%`2ORuo>UA(p*KqEXS b!Nl+O;Hv$^mH?62sy&th&XtAu&jY2CK@5z!l(U7Lx-Wy)mloNFvU7o)H-I5F;7 zefNZn|FMbc*34J$Q*5i7xEcoiWTZF6JVfe+&%e^`e+#4d!XbutOX#Oj qah8Y#8*%D^tc1Gs+A3Z-dXOSMVvi5eB<3(|nk7 O>~cz;0BlM?b03f{~7`g(HfdsIn_m2xea%+ctiaT}C^ci@563>ww_c z4|xJ6h;gxC-zdO_xWoM_77l9*B66Ur6G2c|ADJ+O;~bDx!$&!RvMN*d#JLDf2y&3g zM1WjK8)AE^G5zHfS}KOh4Uiq5v(wL&p*S~c?8`PP4kf;kFdy8O8YeTm$Y4FPw*z3_ zaJx|saHCJ%LTbyE`3ilNVk4Qr>5yU0Em&S$ 9d7mz8%s2jK>wk#iSjz2!lEL;b_oa2O0bEAn-=rs}n6VP=sz4 z6fw;z54#$+&yKAOJ^C{XK8il}&xM%FZFaJTaQG@2QdZ4u;mDGf!BgAT!5!Q;#%~cX zHIvq~*P3VLQNhPKUv#5$6<{6+rM&AnALC$7o9 sf!gL>?D2e}tiRVt2AY z8dabtusS(zhYZgx74u!OTQL+qe(i9GWq}_p;`;nVdNtyh^Y%uEa&1Jjc`PS79+ax) zStK@7suJ|r5Uu9QG=su-3cWE&Lj#UZ_pR{H^l{@G1nnC+`;HwG!lj13?q^@`<;{|Y zJZnLx`)&}-F#QzQ;qGP)#$SjhaL|)VV8IV}Vm>O;+39AxE_jCnu8AI1P)MOzf0lQj zbN)u|2t~YtS8Y1ztE-}GR|a<`SLYgZ(65SUD-6%5z77CzBrS~^4GRd0fw~N=8HN+H zB7tA3?>f3eRQ+htjO)tQCO)v|QL>}28eGOiRwo$`$q&$|*OcLqLf=7CeBj|I<$(kG z*GdXc_-3qeQfu1wx#`anz)k#_MIjle+l}aJvPtX@9&C%Ic#GdS@>PQh(|GkJst60@ zfl3e8^Vl_~RHmIB#=`_3uDLp>qZjXAIPOl}Y~5_bRc4g)>wm=WGHq{X)>5@rfRb&X zdW}t)GS49?M0gILyMS(5Mgc-uPF78zn~j@O?Yj;qK>{iiUYPsgN`qBgzTXGZy( 3nn5 zvG@VF`g&k%XOsEFgAorop^>Tp#72WGHwHA}x#RNHW4jsJ;@!~9TFD_yn1s)?jIe7m zCzzFrFQ(v`v~M8+l^aCkxy`w%EwDC8g!`Z(5pTVhe>N8Uy1M$CyXL^lX}RNkP~u+D zQa(D~=qLur^XH!Cr !B@RFc3j&qO3OV`q`9DFy}80 zq7U11Gobfv8|L4>TD_|}%A9>j+3To`@OpA~uQ0Kirt_nb=} 3r((z0V+j$TC@w8T7M*^Uuj0LG87R8 OX$}RtjZHD#B17MOrM8VJu@QL$*R$vNj>hkY(( c@WUSe;@9S6-L$WVp9~tWm z#y%Kke(%qH&i6ju=k9wxzrQ<9=e*Cnw(EIaj|)il11r?+Sq`LV)w5wM)r{T;QP3)6 zfmBgc x-5Hx%;ALdzbys90yF)sU;EO?rdjX4R}1` zeAxryI5da7-5N`R-Ze!c1zuUR_mt%ekC}Oej^pvEeOyHjOHl9-tMuZ^XEbj~EAm oHS7DodYzZ$*8 zRIWpdgop2eigg9z8iF!}U$8s12iRgLF~$~5>4VyHGD?Z=qP7Zb4!p{O)2`v-b}|xh z9b<^^A!h+w^%BeP{ib7Rd2_yXi!W=se%Z|bsn^XZF*Ju`#>0u{PWFfEH2!n{&S%63 zuI!-Z2hWhYg!dG-r^|e|REu$R=Sv3Cy`-37Ea@Z4w}wmwYz2ovaLJQq+kbjclr`jU&vCB8|(4%D0F>{VN2g)hV~#$IP2Pktxcmk4AORZ;Fc$RE}H29 zaD$anl5NJtKq78KunQTttz5Pbi(}ewnvk~c&3^~4wjSB=v9<%}Od5D9m1N>E3AM_z z{XO@=D;3oc8#VR!n9H9FSp5x4XBTMdgq5|R=@vukzL}wdbze(B>0GkrJ;rd3&(V4p z>$kh`?^SNAP_LJuhC8w$G-^j7^BxDN6Q|kPrcRdz`BNSi+!-ic-dc6!jhPr6k~%j4 zV4+}+TkDolM_75|HBTeldK`^HK8NFR@!26h}e! *m#JiJCh>V4q{0! znCR5zOBUX%XI`HM?F8~WP=CQ7VctG!hA@HCd$DkZ90-kgZUXXsOXMhgWJoRqPkJ3c zy0G6we9fx2$I`1&f*oKm#kNRazzqRrG idKLJrr7n~% ;4Yq*yC2`h|?TDSJzj~ zS`ay$&Ye_t(ml|cFAeR?RQkS$Yw*m@mdXp37lEiGCi_Ay&sK9uPp41guE6v>d3M9i z=U|E?A!w{WsfqO_AOs@8$by5D5X)ldX;79?WVlSg8yCJtvfP>z>4okqFTj&QKPsVl zfFua0{x>DrrQKp)cnr-H5c~SDmDhj4l{+cX^>T`L)B-1;mXEzMmw=3 @q|iaA@57+?FbVNe-Iv;%osUWwCs+1!)#cbrx37KILZ#>$gO(2_OkP|w=hH9E zg$ErN-jrB2slHwMXfhjqCt;lnmu(DeeDUOsgPOo*k11$CwDoh{R~u0)Qn=EG8BOcr zo=x`x+NezU33ZEWXdpM+FDI+W(MZd}GJ(A0=!dlPP81P&D+8P8Pv#tj@WPygOHZUvTaNIzsW15_z|W zv1w@!nN4_R75M?R 6-Ll@iYN+b=*az7H__gcp zn_IQA`hgGm8abCVDeMP7pK@wp%P6*jgNcy!hC)b$+HFnQ!L+q{jMaQ(GK$;7mUCBS zas1Kmy6lLuQ8uFHA`5BcA7al5Gyipra&Q@Jpz$>MCn;if^d~1e@ajL$M+4~I0vtuT z7*fTe^kQ4-?hI_nG?`*wL%Z0!VK8#%L=&|}Cs>iNHu*!%$2DX}6pAgf9kQ8Xv~(@~ z-J&(%--`2Nd|Arwxza%U+Uvi$i>_u62Bqtc8_&st(n|s_;oA!cS-6) zCHZ@sX)#q_LhFvM+DjjsGH&$bZHTd=O)tfK0oWcPSuRH|0vPaLL)&|?>XJpjzay`? zK~AfElse(|si&ADW~J(j@ExMbX}wnC>f2hW+>4B@^G(w@{|T32XghK$Q}|^inVR2v z^C4`h3Eg-L<&sT6UaOQ9o7-oERNXnu6-c}cdgqth%bPmF%Grxl=Mt#d=J;*;$xK|< zGfx=yVc6z~YlLep8j;sV3eiJGG3HI2@YZmAK3oc=uTt%}!!>Pa0$Qe#YcvGN-pNs* zkJ=ja^U|+ihkpvt&!(Q^hgJFIV2&O_VQiO2clrPevab3&R39L2zV6LBvpzJxxtC=R zKe6_N2-rOi-{N9GwCsqI*n`G4nP-d`4P$^|L#}g5eR@+3;3PoP3D?-Iyc?|)K)vIc z-bsd_Qr3W+S^G!ESXEC*nD%@w>XWeSFrsSzDY^|m^5Ks8lfR{>Z 70HB6g8za>R~JIVD0JG0xX$i9YqkyucotOw^p(%D16U zN$L)#(*PsB+uvW~!S0`+FE5%a8~Vt>L|xP*ivv}p ;U8E7`nkF~t6&U-sV;Xnt$S$g zF7^)0NxsTQH&6|0ioW5!l%Upwq3C`?f4`dV=Qf$!P1y-btkr_a!GP-|o8%Az*cB3P zfp-K%jVFE|Q1~XR7a^AXr?CC?SKqh}Y#iB)E) jiQX8WaFh-_ zAM>^C@c>$&|LSV(8KNL*Z>MOa>3R-*2w4o<3G|vvPM5WV1T|2lhp(asM=&~q9bU>j z>oWs8f ;wiiDS-C$P-3J_bh16X z2Qq?f$&jC{MDG*}u<^9Og*ie1B^x%GdP7#)SAgfJEyiIyalD=m%YW`~WjvWhSh?cB z5dT#jBws0x4+(hN;2kg-^X=xo@&1>OhtuXzxxZgfY1Y5A*?5``yF=@9FJH@VWs_Hg zR=KlVplsHr_6m+kd7gNhCRTagOwvHXmLh-|Vh7c~(Q+& +6O*uisw#l}NY7c8*`7dGTw zQo2`RJL#wl<70Bs^yBERxqdmb8yFIKrnDPkpnz2O?%vQXcB^q|buw3m-S77vQNk$= zxlvKo6 ey{%|MG=+lgGP<{&Y^MmrQ-q*6n8Jm6( z5e%t9KE_^xDx3MY2yd2u >rgo<3 zWzU0eaHXojeY~Fw+R|V^idxQO=_uzSuinQ+;kXoRuy (IAH**Jrth;qcTa(A3|!H4)dQE6m~6mhWx@$0U`U-$L4=*)^J!Bj8{q^v z`X>GNRxN5n-VC?U&^6 (ML%c|u2OTAH@i>JZ?Qx4|%=Kf-OsJH7^ zVczJDh1b*loJ(>W4DcR10fEWt(t MV!`~h_8cY9~v-sJ=S2{CAW7%H5{dps>fd_+bL*pS6XG~)FCw*xEzd*?(YDl|=! zuEi(E!IM7oO0KMYT}Maz?(c&PxqO;@qvQ$Z?<=8@_XugaFesn%a>1GQi_~Wz @mwoF z!zl-lk<|qot3vM5CO#nDC)~FG8I(=KILvH@y5`T @M|Kq>J(6)TBrwTBl4 z Rb(l&?X!MStMt$M@fQQ>@}|oDAD1 zN5-Se!rY$UCbmLy>=LJS?|(Sg)z1jMIC1-&tftMBu~Jp# M(O((C1+IDKR=W}m(` z+@1T_FVJ8djRU;i(9cY$f!aId;2@Wh>L7WPr%t0?BE3?asM#B_Am3v!3nFS#R*UHT zp8t-V12teHFOHHL>R+JZY4WQQo^=x*Sx rKa@c<~`%pKzX8d3Xl;u_5xiCHAMyOr*RNH4|jP0heEJD63tPKeD zo*T9WHFf#L`WGlc5|SRiZR8BV?py3?90+bTHr2fX!&z
Qj>*^@%f$+jNVgdIPldU?{CJ;dwFHPgt&BbevSC(%jCa7#n_AY?ii zwSRjJaL}z%0V+YMtq5X-;`jt6*ZJ@O!Z)EC @32B^Ut-9JSrecEZlvNbXQne*M(dvB&EehUb1gD^LqE#d!jpA^zj-#H)1VZo`1 zH!0I*J@06Bqdnqh*)YUAhB+xoAa=-Q>@1tZr8t=fNCgMIen!uQc`aq0?Z~NE=J}?0 zRBmjr5Lhd9$Jq0P)!>z6BV*WTs<1-iQ@Z40Cc!(<^$-NYS96itw{3#0V9KbT($pT3 zPHXDvxvdo d#C zUE5A)!tZ~m+g9b9-kGYQH$*p9^Zzx4IVTfhe9e4a=7f0F8;8)R^%@oxL2EgomoRD^ z@`a4gt{t~K)%)&pj#yl#iwu*J!LpfAWaTqZI_pvq5ZYr$>unlBMv_RH(P}<`P@eQs z?;*?cI@ykJh9eJa`=uiaMDM1YDXh**3oFt&a#q~|V1@7(#!O_km@mNHKk^=@Aop3- z)~q%P4o0GPPPd}DCN9S*FV%h~I8G2u<%Xmz=sq8h{O8B*Eh~w)t6mP>ArF37*b@O^ z$ckd_DV{IAb}R8hOj}2WhyEaD{fbGBIF7Z?n a7ysk`^OgQ#{NOn3i&rJZBGeSTtiYzPPPQdOWhe z!p=~L=~GXsg8T}8I(5lkpuzC(AMy{qPSc+uz cQcgPVMBBn`;hYqr)0v| zV>DGHxvlbg*5fakd`{V#Ka{J+Rrol<1|GDG+CfH?d9IVH==!hf=-H^GaR+cN5Zr5$ z^`JyTWP9Dn$DqTdi>j^Eqn$b!))PMh$ni{^UX8TeU=uL2Lx-h= c7R}(UE)?u{OH;~vu&|ptz{rh8r1cVB5c|iUSf6pQ)%y(fh*-u zA>hdDadc>Lf?VLcjH`%6r!~9Kg<~oWEd=_|!eKrR_z%oRTo;O$Mg^N)I75m~HTp_q zFMugSezc7-6CqgCF7|nLi^zJ){jRCGBSwe=dWQOrFNmkvJ886S+57r)(YV6!cg&5& zJU{=5C2OpD7xcaStHRVQq-Q0~Ql2#`7 8$4tDjQT8-<=J`H34tbjJP_Ajhvw$je*Bbwo;5r}< zJSk6aU8hZR76nJUDcs{P_5ckAy8C>T29Z3nE58hg0_uhLg@Uz%NC?M;&tFjXTTMMu zR>0G+F|9yZoa7@*&qsCJkD|RAmyR)r?%(4sX^$L%zq42wd8@%sj!?JF;Tp}LZum{^ z2CY;v>awK-2ZEeLs+h_y>LdkB8P+dvK>3@E_b>1G6c7xCIHg7PZpi`JJQeVjBe_6) z+NA$v%>Q@+=!efU{kI07(}pv(ucy*cN9E=g=K} x!`Y>_F;xApT!VU?$@Q`P;K&xxdzBYu`RUG!xxUM?4L zxYqIVCGEjFhsc-+Buw^Ea4u-~u$+8yH!b9xjW(4(xF-+rBX73a!9yDd&mzqePaqn- zw*r#^Lg>tv(jA)=;$HMZLk)AQF65t?e6neeyTUhu*$B?gxVqDLr75Ck!vyA+tvk@z zlI2$fPawYml#OBBs8zq){+Y+81>}ASJ9>PBm!;|LvWDb!Wn3H?`cMq4csy!Osp9Zf z{PhM{rNk#(G-MYwG3H?mN}o=L29Ro9$HsTR2+C5wpCyYO! z{=#L<=w7v`T36 tx=5VdR|rp)GiH_TH3SkCTuB zr!qtZb@Y42A~B)!dGLwNi|VMJN%h`vr44c9s!i!sK*J8eIjqT791Blh4b4YKF~>qH z{Uz1_mLO~zy#1j_c~ix(h(b`}O>^0d{J`ux%c(pFcNggOZIBIURZ<${YCkPM3xj+W z#ndvZ^X;Xh=6DKk`Q*MBjv(u9p8eQcmJc79O=)0z%;{s;3htgUNkslGEe)QmLT0A- zC0-z@QF5p1lGi@!_o7q15-o!UO6hyPgKJrxh9Gp%VfCZp9L` ;hibdhp zNaXB2J%xkD^P%MSnXSdZJ4;8>dhW(BA3R(5|03p>vuc_{hi#Zvd#hdn8 >yj`>MDLKdLWTjYc&|jjYJl}n9?fO8Asycj~Uho3%vcRw2SqI_x`qKn w@_H`U;g~H6FZWMPzyfrk|qM! ziU3n1^ho|wDsoPl#0aL>sAFkxf-t1#`xZ%A1M)OBGJWMlLkIiF5Y@B3=t-&WnMH0$ zSfMu eQLMEl!~~0HcdYq0LQc6AwQdX% zPd3L5O&xfLqqzO&?qaiU%6i ON$8cZ|;zu6j< z{n7Noxd2WFv8F~(wQSB;$AO3mcM4LwefCu$N=wJ^-PK7%O=`F*zxT2x&wd_zHQfQA zOzzwiBFWt^ @!yZ6)T9evIc#ep zZgdYU-ih&dd(9G|^fXV5kN@S%2atDUi08GO-MW{<&QGnceab=~un%6!?pS_<>Q?^w z&>ijCm0vfc$3}YT#D~l@g7b2KYh|DEAg3N-Y4Qd>v}?BJ!* IKFY#?IXcN8z$A3jpr6_;JpxFv9xSUgNm`1DIUV{_`kZoj5n2J7&G_1o<&6lLvip z#Pj>|!?&VNrbbKFB+qB;7bPJx&&(Z6MOk5IKsogB`bKaTjY|GdLkoh7wrS#(;8u4c zMLU7q<44pvh(1JZtU2W!p1*iUHjdRCRtFHzFgPaMDgc~Z-6i!#BH4!jF2((s?YOKz z*|9jaUG#BLHc(MVXtTZk;f47htUOH`Wz55ZpNhOw4?l31F=y?fypH7QMvX&Rw-X48 zqjfju%R~EtQIcxoZo7!$rc*JbEMBI{Yy_i=Ep`F~3x3XB13i(x6H{ tPFy &aItO zAkvP6TRh4**lY4R!vMAv)ptbsf?7!?TN#^T^V)FFK~E%xLsf+qcn3w>H_}krZ4Zo! z`S(UIf#4K;CfwJASG(pNV5_;A`{V(3 i)9iFiU|4NwM68aTf-i@zNN2DAaE-NJ>Gd;}zmj;5e zlQYx<(N~*nvjug_1WQcCxp=?WzW%S#-mBIL(3cFz)_d}mcc0tGOJ}V-qsCB<+LSq{ zP~BUbMT!;Xs8>b9cNF8)LtOf~zW>}^wQ_CNt$#iy`cjX5)&@9zoq&v&V 3c>@#5Mi%5vNPQ3cs{mJ~AziLr z^vb=X2eX6XUKd!Nzh0oPlmg_y9QpS0uQ11g@j_PH3@k9uJ1ai97EIB^sA7laHuvza z7$_p7?Tu94f7x8w_Jm@!W(*_+N{jaZe82fNisFwzKm*X)s>UqFapfxqr)VPv3=^;# z_%JRu&hjY3C85mqJ$D_os4f{?Cxqt2Zlf6S{Jx6osCSUv#qR%9LeMfH@lBnW-u>98 z^V(YY21qVu+sm5bM+#3}`Po)y#JxZx@35gvO#y z>3jhXl!_mOwUo-v-JGj4wxhIvLCMv%Ql%(31?4o-*|Tx3lGx@?fJ#E;Nx|9(chg zI7CajTUm6Odi>uJTJF1Rt2q5sC9hQ@#8bx{qiVp;-E!d?b}(2jr`Q;OUw&M0jtAFj zP;HEGDqk$Kno1|jZ|9Vu)=_V`&nVH6sen_3{{#@UGa+*9VskBU;DHgY!+mt!BJesU zcg|C|KsL@?+rHJt4A$-sQ;EEW+vgGWPLW8(!39^+Yk&JFS=HL*mSg^x{vL$; P_%ZKOic-Z-fQdZ8?O>CO^dhz}T{LR%nT3!Pnj5W9%97@T%q6 zz6Zk#PC4+YBi4wjU7iRHKAc6=%^JlV`_;Q0@^#AKbV)(QQN_@PtsVctT2y7P|MqC} zzL3v4T5XNE0SQ7da~5=!VD}2%A|)ULfp-Cb=Ik7Z8R_ho1_rMwK?bCQwhq-ny#dpt z!$=Aq4qC2Twlb6kNeq8;F8KyHgSHp-+b?Jg4goQ^{{>I+et^MCU2Ds&!Kd_G7-MAC zgP+ Ph*fzH2JlGut!(j1aTcwEF|sdk;T1JplXkQ16?{|1&Xl4 z;lMuRxn;}Tj62b44(Y$^GY4iR`!~idJX(BT`A*)m{)*ahduGwx(cL`0rh;8iim_Rh zi^FMPA5)xbM=LseHF!^hz$N0I$ip^56X+7;K38O{hu_E1`=&KcK_H^IWzC}swizez zy0%o#QvSsb=pYxfN-W#E@i|cBLh0J&B!a8jI?3zWy-6KxqlL9np8w=pyZoSAoa2U> z0i^{`*ISjWpG^iV+l$lN!D!TZ^9>`I-|+$O>;E0m=qT<&g)d#Lll^$zO2LF0o|o{| z(ctkAHCIO&Kci}zH#~i7bI0Iqp3lvkEh5$CfR@1Hc&}EZ5A@REj22dL+}(LbzhSq9 zR^jg{kk$Lr1GsqHgnab&j%5ZeHB!S@Df*&gy8V&UmC?&_m0?F>GXg+sOTklZh1h_{ zpBzgCm(c!)W=wD(<;^svzx2ANb7~o%M!0fMs0E^Cd#%;~VCUc!YR86~^#BB!B{YfO z*Qq}|k+ZAw`H5`?rMRWdZ;y|M3H+QJx#C}4%X!8<%?&Ta7MM9NdypjYh*M%DU`%BU z084HauLPlqaR*RD4*CGmw5Vo`t4j{h1-lYotk(j(G{Z-J*vr5hT_j;#zHh`$vZ_zr zS?tp^0HO}Iv0@Gq$~CU-tkc^1`($!GQr!if)Uwli2BP{Q{7jhU<(a-;?*;`CvjMrz z`=gvKmV?GGMWom-Cc8F84Ki2|@?=-jPymE}6bk^9N&uG$^KGw1AD%~m`9Pf&43jJI z)@@q8{pk)tQ7li;@Y&k&85OL{+Y((zg`o| 9Eyek&`l&I*U+l~59Ee=qRs*}2iz8OP)lw}j?K`wjFap#Cd&TZ(3 zR0DkYonsKfg(xQvle7KrpXD|7 lJ=Z_8FwI3ppm0)}tT4wZV%=^Drs!CCvv+v ;Kj`bY#JKU3iN-AjYJ)}2VaLf$37-I`hR(pRO{bj9t zu>sC7B@?ycv`T#?YPqI$pgi66{G!TA=JUB%v5duTt|=RRCzA;?3oF^qRNXwVBQq9w zJC*oqDRVmJ Yf*$jpAqh zl?NZY60dt5p9SyNa9)&Koj}7ew+bI7E7Di5GOLAbC7zB(d9GBddrSuEkjLeRHzf1! zQ1AsjV`h3BLT;D6eB7T+(KWGk!Co!~ES@XJr*#u^>Pe|YOM1A?+H$EIQA>UZ2(J~J zd>&bl28lZGmZQ2Qw{)*54O>Kd LdT}_7KZRHL$>qK3r+>oHJ*{ruy?wYv_Z-o@xMq7v$FG_Hlfh8#uz1vR*5j zhl*09pf^=-3~bzRWF)V?lf@`;av X%w;MQ`% zXF7cj?W>vUz$N*XL71W>o#*#f{d8u<)%&~OFZ@3wzyDA3=U%&~t!DX`OjMC+LsD32 z_N7S4shg2QMmdk%ue E+;D@k|ZLfC2-~8GDLY;YV~RFBZI>XmM99k#MA?WZv+M g4V^S^UUmFdJpk;56Hk#(b#`@t{NZv>eH#x~Mne=-#|6DE`tXLBa2tQboD- z@52;JtvTwY52HMkWOrI@p6Ju}y*~<~=3|$W`1i~ozz!-jxfcWT;Lq!n`3EQwHTvNn z5jf#y$N+CW!+?jg71qaKFp;k^Z}Df=?h=*L{Kj?^oeM B8N Wbeg*zC|fBkT}a7Ua~2IsF2()K|USi+`o4_^9`wp(?JUOd?sfdMr^?a4smp%f5cH z(@s+vS14P_TtnRTLsXb9gxN4b23YxssuaOF!PwP49F+?w5?U`J<-u>Mj6@tR+YVyT z=641|QH-4HqMkZVjr&x2_#w_Tyx|=Z>+|fi2f0$HB#9B9=1+dKowakCe_UZXiHZ z@}sm3dq@fZErd_9Rq-o=OHn(e%bd4;8A1>ay3xWn$$BOezy3;^Y&1MAm+chLa75jC zN@9p}eJu_7Rc^k(E>$h&qn#K$MTr~;zf`Q4GYKQgq #`t6KCyVzqCMz@aRsyW^HarB{M5#uu-k8cGv$i8_A zI7bf}EW?fN_iZqDy%?T23d%GZph)4{Z($dybmMX|&!??6DcRibQ=|_DA*Dm&g}F9C z1;SYzt Ck4be0o^)#a|;K8pekx}CE^m>Nef2UL62~3j+66OxHo37BB&8O&>-Kw zf bIX%zAyNUGWgLn-A3j+Nq(|^>!&M5fC^I%55 zScy?@b{ex-F1T0JgZ$#fM0{rqa$~c &m)Up`fRGWlON} X!fgc zy~5={guK7^cTMcmdKr;=WAR?H5tde$EIb=4Pyy;;M@9j5S_=A1wKULp{Qp0sG;JA- zFZIR}*D{AHoh-RfA@h^hl%8qD@MenN>+x%})Gj IRDpbvgNV*>l>IAuF? z3t f~-to{t*zOWXkFd_wqV&ylG&|Ko%p>{cvG<*nNS!s=$hFr3%<<9l?QV*p zYpc-Yen3iPHq#Cq$<6m|rcJ)a=$*eg1}K8y){29^F9WU84Kf4^B9OnfIJuWM{;O!1 zyUTv99s3bs9TTGeNib!387lU{J9$+Wxz7ZG;T-^F!U`PF_`5o9KEJ;?O!8VQw*2Cf z4{HwOyRx1jl!+uX#+@J36iCHxm}B{*|6fY)9}$i}lkNBw!P6?a5}RwWC-+1-MsHPu zd4qR)K3}Fe6k&4=gqOCOR_2NVczmC^9cJA nPkaYjC23x-8lXTmcW1#qQ(i)(%`|b=j%?fa>ON4V^QSJ#qKe; zE2;jLh7M}n(%`mq5i`TfY!=VfiD7SK_c&Na*TjQqny{}3L8X|jlgyELPKH@F&zt|y z2CH}V%269Znkh4W$F=txp+3#bA~PkX b0+m|IhU)w6zj9HYhop_}|7Gi}Q&V7;$Q~G3KJ}?6 zX(=vC?xtw_{MOSSH0Ac1 KYY zd{s#V)r9IV{P_Vhyd2v7-a*Z$UQ9_~S1L98Nb1_>T(oUccK5|xC%oP1fRPvP?KrIy zfxwdV3>Xqol(YOB>PnwPx#a<#WxNz|XQm=RU>DFl$6^Lr-E %I2%{WX(q (-C2CJc*kIBV=i7f>-G8JtLTnj;Gjv|qx351{&@SY> z_DA1UyvtnqYjo(T*uxJ-Ju%ux!&v_TZzCdmm$eEWx@@woOB-rLdAbC&)P@$Tun2D> zqud%3kG}hGgQWWAdq*`EOqGwd4mB5_kirEP>;yH8QUZybYkrZUfy =@G zmO122%vT(}r5mxR+U`_f zs^@Zc3mEJ9`g_wVUEc$@C2pG1zpQ|0L$9N7l#t(XC*mkh5(ShSfAs{it_po9!pusM zQv8L#uJI689b6mV!#>$>h;xJY<0@jGN$>gMCAQlP+{4dZ(+j !MG0 g0=PMp37MQs4L9IQnl|YJ1YMNlu72ncWw`0zXHvJ; sQnMf#LQH8o@G!@wbG6r(?>`tq)c#bev_z;ahyA2R`gJdAqF%1z%p}J1AT` zS4RVj&E!CydId-nD&D@X&T#|hOb12;**#dfb%Esel~v}-qgAsNAQOE2??=lYKfd*3 z`pJk~msS3fOOx}VuhYCQKAjueu1fY@?CCNn2!DTy{pq0!KSE)EYgU|gMKIVce;W~j zp1qFbCv{ACVXrYKykwi$l;?ooeX{yY2o1@$xEsuen83sMW|{T#T!wfn@Sm&r&D$9D z&6h?i*AR(~9Xb=NwI`7U5WO%BJ^jZ+QUPD q zoTIR9yCww>xFtWtECxb$CI4&Lu6OVJa7yJKF=LxZho%NBg!`?{#h3=(JLF&}bH7EC zym{7KLCwL%;feD*>3j+oNwiftg_!V^HUxB%on!WVhPGdC2J$^E8}umD?*!U*5t bl8J#q02e1UIE3m Q6utS&} zZTmGNwZn-RTtE;)(Ul-&40{}}Y40ot;^^(1O3XVe6K*p{XM! +qrz+T z0HTuj6rq<|Q}yMqp3QnO#ytsnr4_tUnAXMHL{`(FW$qnCy*fF55U*uhS$ut3oDc6%*~TN6uEr`L^HR2RTw;OwpKCQ@ge zy0&Lsse*$;>)p`0!SvIn71??tS|g=Rmq|X5_PJ&Y@{?e#-uy^ceOsJwe`%_q-bm?~ zpkYd&Yx$(c@^A0uQHz=$=kro*ahKm=gG-xRhMvx3!u7SP#t=^dM~2&P8oFyUc`Bsu zIIa@)qAyKY0DeDID3f_>P5;6rzwE0Zt^8!vJzV!%IqJx0?Y5nnp%bVRbdYnCEIE50 zFaF}Jhw;Zap 2{0x<8}SVbti;8 z7ixObWbP`dI=%SX?n^UaqX<;Jy7qpYq#$2liJfk+kg@+vd-zcK$A;OI2m6Yf*k99G zeS=c_lQVl6y@)f5^&Dd%*{Il`C}~iB;q2s7I54E=4X=8qy8Q9`DIF$ Ee Do^^2Vx&YDmrMbbm%X~N6|O<;wKQEG zJsppn?gO41Bp-dEixpPVPx_=!{Bb?8KJIcA`RdMSVU^s;u9cbgFI@#dwt|tww9ISo zQ9wY}`(ciz!vw)gH4^@jJU3*JNH$7E^i}Is|EidpY>+Z+3z69*+aq`d-#fqa7P}B2 zM6MBB=EZLO{Q>=l(IwNH!Lwm<>B`$_F&P^T{rjuZi+rlXI#$lp>RV3*=m++42GC08 zbyh`QBfaN~I4f*U5WNs}jlDtPw*_^edM&;IU?1#(SV}z%VF?o>zX!Kvb fojMm%Fllo9;Y@zHNbPPO$`_7}6<<=2a_!h}5^g*JNrH z&}-s>8Irbu?@W$o$^Nd3M*n>~3^0@r|1=l2pJ+v X1gxS}mslgy#w(UR<|>2$acG!r1QLb(@{J^cjPZSt}Fx zV0WE*sq6E=9T-qFHIFvD=+SM?hNA=c7!bXsb6i(dx#DxZB~Z {B|>o9#Zv|7K4#MDWVXL~1_2mv(#9*Rf3Yk+GF%c}Pp z%>e~r#}|xt{eb|>w73Ad;xV9TN2%aU=!c7*JFWcKkubMNR4l=(2_=tg;+KvJE)Xur zb=5GHZ(zi8ZAn1Bvs9Z(PE#h@U)`JfZ)H;ESbA*96XFfEg>Kbc&g=b#yYB+Q7cz%; z83osQBNn1 !XrilLMv>^ Jji->c9Pim%tJo}Dujcw_z^9-8#k%Q zB%@Gj6e^HR41|0~0;xtASbRGK_nuj~0dqRVZQO~7u6cwv45sZKI+1)9uHrx2Chr_V z{jcnT oOA|OQO|fQR-7nr#sUhEbqZ4r8);GBQ;;j~&LG6D1Ep=KU8F^?&6v!%~ zRe?GCa#0xVW76v!y4M~wN+KIj46`~0k)q{|>e#9X*W*WNR=**avhGCI20jv+)(KGo z6_c!VoC>jU5UFv>X#>1q0ogs&La %NK-u z(C}a3(37bPvB-k0fN~TWHu~Adk~*Eyoyj)leoi#2T~eg*^NVN@NmI%JgCnu_)K dOu&!=i|8+ap{CWz>~9A zwYQ8%+GE8Q6AjfuwCoPhSQ9ZZ_^8CBiaSK@rYe;GXVnN2=^e%R>ve>(^j6*t5w?Zr z`6c3 )>e3kd4yi^>;lO@ksSb>MJmMC?oUU(U7}K&M%TBdYGQr9 zIfQrvF{%a+I7Kr%NPhK3DD+zOe_o>3ViaOy>=2~7qz~k(*`jyqtUPYnfz3TkMlDzH zlx(1Dj!=vph2hSAepi8KI|5Vc_5C4;tVCaZ9dqQUdj{4+R{izeH1aVm^X`_z9{q7I zdg>v$6;U>zdig(aq8M49ag1U-=DipCasr!1YueWIgXOwGGKZ!uL`ih>>g$NjR~X2k zvDt9Zaj4Hjd6*8xbwh8ob*|M;OmCQ1Eo<6>I}^JO_){!tLGad`@R1STzV$G6L70fY zMJjP_iVqajXGsTIgwz|mB4CsF*=#aUXch7eg);|9Qrje%bitw`hY-BCRYp(ec TF )Y456Gpzbo(7}(xUYxc<>smQE1#LaPjm=}m2UTN|B z^eWOGhoegFN7_t&)@rNq^4|LdvI8#5{`;OY@S 4q(v!mBgG)Xog{8>&`<)P2A}n`n%VIpC zb=ak{m}s(xx%6BpPw;b9(4UTpQSj%-xx+j4zPa&viK7i}&LHwiLe(aYk{T1G`Q7X3 zW9C296}`v`3?w73w9mI8+~)Kk?=r8~hbB#g0EnowR(nth3vSO&qAgFiIZmfQ@}TZ5O-H~07h%+O1rv)GlU68n)j_&N{e z<>G{+$Hrbksgm-s@$!*Iv5I$mjjgj`t?-kaFVHq74 _Z6#Us+zBn`> zftl2+B>?Dg{&_ngR`L2fCb93*m%#nsbg6WpX2jeD73dKhV@l}YJ1IZH0Af92NQ4~# zH^RI2bB7R>ZC_L;TLDWLl@BQpiKUP>=Mf;K2_~he5A%v1kk}6fZdS2-J#%+4*Tj1k zCQusHJ|KoqY6IJ$CgLBd{RdTmg(x=Mp}&4}Fj$&*XwcVh&jtVY7n|Nos{3T1Rmk~- z$6xG%M?z{D=N(YQ^6}b=L52%D6_?U=D@3Y T(t@*(roe1fl^fW`1M7x@b23NZW`D)?v0CTb&A$P7mh3;6>GlT!?dUy zcfN$l-2-=ddEF}<#sj7S8Kdiz3wgD6tmPOQgGc|M`UtOHUZ@vQ-~MQnNN&JXzWC?! zRyoZF*2PI<1k7r9+tkCM5%72GYN{WuV#Wa!#HcY$r$`u@EZ>R0>8w5rknR=b7XgQU zo&2^Ljo4nk;SwHVU1i7$0|!C&ISnd$=0mDtPlx;FI&?#}X>qV+EB?{*y*HzYdO`m1 zdc_>upj)`tnH%N}p202hl}(~D4kA;+9fzL%=d&3!AGWx&H*ipXP4!}vCA`(tg?%xw zCO&@9qrh?6Y3{14`s_0+;s;cwlBm}|u?_C_`Yr=~O0Sad=XsKKh;Gh}XOxAFZ4FD; z;=PwD IuM$^LBh18zolTdx+B<%egzv4ACZlm|cX-_a{-@ST>gdHBJTdJzGMOt|m zv8tm)5>U}lo-W0~^~~qM@ov>>)ScV&0~v=@#J|jvAzNa)X%%R|^`5b)58%;0d8*LJ ztlJpdCLa(9q-3uX2ydT0Uro^4 zs;>LY;(XZPWP-btj{@@e9A7Ob2Q}~Xr$7(fDc(e<;`~$DW>L4R;jQql52k5%aCb3v zcGW4}Rh$>M@)RHkB_K1GLXWF{z4^-h(;=YAZJg6P@N8ef7b`#MZG46zu-O|7C+?Rc z>wVYVL>?=WE9y)IzXEsgLE~caWsLn57TQj+r+zeU&;UHgf+Zru-Pk4oWP!8_JfRcu zBJAfULXdCn*0jyi+JyQTrdlpQL!!M_3scOc21;K#w$T3EXr;gq4(rCOWrQr}Pn)OE zRwt*K^m-)UiNlHAszXq#qvdXnhxX5w2kdo-zEq4r&%MrQmpuFKj$pTnuWF;m45kB4 z9HPYh{Wtqp5)a*x{$T>$+% KT;&v9e9|~p*}C`6&=(n z`S653bM+ i?Piz|W)OecRHDZ_T_UDWQ3GaMgtl+Y8+75&UWZXvEExqD5I!T9@X2 z=mA(f#b*y* T;XI}Ww^%4d-b^p2@(_4#UatJT zFsy?K{LRwQQ=RqlbZx?hD+57Ye)fg-Y>kk6N8l&nUejxMLD-~>KQg6(%>}=@>`yoY zpErrc*;co9v|X2S4cY_fGrUI^G(>$MtW=zP6U&!yT|ThqR%3q3Uq!Fr%=sTrBGnCA zCV#O47z-Lu^18BGUIy;s=YVu;4fZ!?5yycAw5kD!BNIjZh5lvj-M{Y#>^y6Ge6LAV zXQzs)3SRi7#dEl=Hh=tWm4{2?&F6Pp-mcVeaOTQiO6_A5F3 ;8!<-%r$Kt z>`Xuk`4oTa)==?I`x_HMdk?Ja@Y#ilD8XvA5pbe@sU!^uJ~^V)zYu5ZYV9!f2Cw6e zJoS#9BVShKY2DGo4XoR(x{&CZc`r>egiXvz0PAWs!Ta&9Cjf3p>MFo>N}KfM;`IUY?h5*YK=o z4&9H{@KL6ADmM>TU9 an2DJPP~fSr&LVnd==%JKpV zy*KK?fLwIUk~#TAD&%yC4eE!~uk$>>m8sX@Y0ISUOZJl0x`Fk{1v_r~djR5e3A7n> zT>{gvzXd3wU=kkWT;wy#TPnPJ_{21@JXbd!%^&u&{Nk4WXzS&+8ogfbyK=g0=pu)r zriay+w_sPwnmglI@#ugLk8Fm0&1mqG_=!uE(O!@U430r69=zp$A%R__5^Ja?o2 z@0}b8#M$%BO@LKRnFSs0(PF2JjH&U!-bUHe@)WX<6Wsln`)um)22UzVaqul{c|{w% z2>Vbc1_)SFB*F3$an)y>Qzp*;`MCftcx4Ah_7D3aZ`<+SeYuqUQDbH}{U)^8Xr_W( z?J3b;`6N%)i2Q6r_2-r`EEnttTdY?w7s=tD2_Xpkrj0$8glvFUce>%2e3ZjVOA5W* z8Rkh=Q}#>#f($wMWZZgpwU-7AEuto_xP2^Sb*iY};tcn*7p|knOzfL8WAjFbLdcNz zY5b80h;dbpl9J*u)Egjt&Hk$0){EE3bPK_ntBgS5!2kz%!}d*wdKIiKB6^>1tPnY? z)f=%E3eu>~-}cQ7ndCw2a&y-x%D7!YHHCosR_P44_w7K}hr{xA=DeSK_#b ?8*>rQaa!^x=1HwrDuSz0)e((zT2sI`1$F`c0O|Rfd z>T9JV+rZGLFO*^S^1Sg=GoxI=2(xLgnxW!OY&xVWe^i>luLUyaSUa#~=Iii!{d|cM zh4|o^e#`q*(;X3gynz(Zk#NfZ65M%-xTLk~NH>mA6M1Vk{(YEq8 L=sg@q&%NQ`A;1-X z8G<034L12t`JfJ_W7n=a{io?FmbZitdbOCXCbU5tgAc?uiMlXN 8#0$W?_Jk^F~#41(RN+mLQm-RRQgFC zoIAi^2?>U?UDNb$DAc(pa!}Qa?>2+- Eih%K`a&)jiXK#tucV=u>m%bT0_hf zM>J(}Y%oaUR)$ie%0avQ??_G2>C8Z}`aGi{#OfgQ-rH|hH(r=sC|#u?r?ig(y|#*` z%4K&16@14<6aGZ`!k+PXHRiAEZa217czj$?wT&%+7`LP3k!cBkQqvIRtCqW1%W55w z&_DY2 SgV!Q6s-DbDG$nG8{hC;h|EZ$w@?ayjpmt Rv)ABY1nVid} zhlIFVz&u0L0py`VbLKbU)YDIXU&^aMUtR+YR4sqVZ+)_`=a+684njXU1t%h>eRd#5 zTKT6n`{NAS<~EYyB>xrm!XaU+U$w}_;T2yUJqS8>2j{1~H03!BN9@ZT&COCUr2J94 zbeDP`(?G*-t<}I9V;||&C`f~AbtZH$H^ZuH;+i-9j~8GwT8!p+&$FM`>>kaL29!&Q zDjL|J_4JIj)HPTWQoqADA`7JYSf`s(AGYl6b_ z>DLtgJRGJ &xUFIxYiXEOmdl!`+P=88LlA8Q*FK%E*1`{+dlsejNnmEbxXnx HE zUxD*JRS|tw5ZgTHk$N2)+Y@qkV)#(n_HCf8@7)~^kvvQlEX{f?3_x{{oVPJhc$1OE z`eu~=s_Lrg)@}X(`RSs}L*2&%R3*q`hCCT1h4J7gVu|K~iHAarw4zJYK<1GKstbK) zAHbb$$b+OWfEe~`0ery*v?D}hoJ}Z(NDnAHDq5lz1R*!UNSB^$=b%#O!k>&&?)b_d znULU=k#4Eqh468(gqr!iW!x~-FDhLmjpsQZfM=;=D7v2s`0$mhJ0w8iOUUd3&nR^5 z&G&Ii1?Tf>yH#O|uxgWhFRZ>?1_M?3>wEogNrnVE5Y=~cVy;wO xvWiv^+pvn 9s(K7w8TsqNxQ>Rk@zI&9-5uCq_S}1zCCA>;kI>rb>oW(x;0~g z01Qo~sY+PDt%zydRvU7 #%?enw{gPWa0e=9@&Zucn zar~a2K^ADIf7^YNbpFd VXA_*< sFHo*W`Q&zSNyT&Wwo2#Ko2UuU~5?Z$jw-R3zaj`+lV_nF~o4>bDqV5AI1h( zGx9%f8byp=-v!!{SSkCg9Hh7vd%jyihwaQKqw?W7oNq(bFxl@#Drw9}i{c_sjiar! z-%&&wV){?F@3puU7m0^Jml1!G07HaH6G06aUb_-;Oo*-(dr9Gc-|!?AQpMKT-y#Jf zEQ6){&(<1CK0uMjU0FIb+10n&Cywn{r}7Eh@%btqK$8^hzyF280vLpVc?vdrl{;)e zMc}RF?kg?NqVoag>>P1I+1sBzi#X-GPR1RNlb;k}U7F2CZat0E_;mgPh{$WFNN!X) z_Y*SZ3;qId`zv907c_Rx^!Y9)++eP#0vguuuArD(Zc7XNBY?ET_|3P7LI4pwp-eTx z^6^y+^Dy)L?|O}7X9UQK@#eDSQj~5kSc0NgvrR;7bxj*hl&&j+5+c&HK|>!oDAboR z&XCc4uNiC1L}p0opHgNZ>rE*Amx~uVFUZFNFw$wMSx2YBRD;;r7@NeCxy0$H7yN >hiLAL9Q$-r;n$kAxf)p;sx0?FJ+_%8Q)=3@HU@PB}u>9xHz-xyn;8G%FcD@ zGraxKb*naC=nvS~91{j-PS7Ml1OSP+1_Oc$s4!F<67X^VAW&wfdN=U`